数据变更-数据库与缓存数据一致性问题

1,060 阅读3分钟

在用到缓存的场景中,大多数是用来分担请求对DB的压力存在的。

那么就一定会存在到数据更新,这是就涉及到缓存和数据库存储双写的问题,那么究竟应该如何解决一致性问题呢?

经典 Cache Aside Pattern

缓存+数据库读写的模式

  • 读取:首先读取缓存,在缓存没有命中的情况下。然后读取数据库,返回请求同时将数据库的值存入缓存。
  • 更新:先更新数据库,在删除缓存

为什么是更新数据库,然后在删除缓存呢?

我们先来看一下在修改DB同步更新缓存的情况

  • 1 先更新数据库,在更新缓存 存在问题

在多个线程同时操作的形况下,更新数据库成功。此时另外一个线程也进行更新数据库操作,这时候是会存在短暂的不一致性。如果你的业务对数据一致性的要求不高,那没问题,可以使用这个方案,但是结果的准确性就会收到影响。在没有更新缓存的时候另外的线程读取数据,优先查找缓存,此时会读取未更新的数据。

  • 2 先更新缓存,在更新数据库 存在问题

同样会出现数据不准确的现象,因为不直接控制cpu的多线程,存在数据库阻塞导致先更新了缓存但是数据库后更新。

  • 3 先删除缓存,再更新数据库 存在问题

同样的多线程操作,第一个线程进入删除缓存,此时第二个线程操作查询,没有命中缓存直接读取数据库并更新缓存,之后第一个线程继续更新数据库。

  • 4 先更新数据库,再删除缓存

    • 保证高性能,就要容忍一定程度的延迟

    • 不考虑高性能,只考虑数据一致,加锁--多个操作步骤变成原子操作

先删除数据库,此时有其他线程读取数据,还是之前的缓存数据,读取到为更新的缓存。此时为了保证数据一致性,多个步骤变成原子操。单机可以采用加锁操作,分布式系统用分布式锁。

更新数据库,读取数据库binlog记录异步更新

当存在异常情况下,之前的方法处理都比较麻烦。

对于异常情况可以通过从库的binlog异步补偿淘汰缓存达成最终一直性。

这里涉及到数据库的主从复制,当sql语句操作的时候会以日志的的形式保存,需要手动打开。

什么意思呢?当进行数据变动的时候,业务代码不去处理缓存。通过日志形式监听数据库的数据变动,通过java程序异步的同步到缓存当中。

就是说业务代码不考虑到底进行了几次数据库操作,缓存只需要将数据库最终的结果。