参考文献

数据库缓存数据不一致

一致性

  • “⼀致性”包含了两种情况:
    • 缓存中有数据,那么,缓存的数据值需要和数据库中的值相同;
    • 缓存中本⾝没有数据,那么,数据库中的值必须是最新值.

产生的原因

  • 数据库缓存不一致问题通常是由于缓存和数据库的数据更新不同步或者同步出现问题导致的.
    • 并发更新:在多个并发线程同时更新数据时,可能会出现数据不一致的问题.例如,一个线程更新了数据库中的数据,但是缓存中的数据并没有及时更新,这时另一个线程读取缓存中的数据就会与数据库中的数据不一致.
    • 缓存更新失败:在更新缓存时,如果由于网络或其他原因导致缓存更新失败,就可能会导致缓存和数据库中的数据不一致.
    • 数据库更新失败:在更新数据库时,如果由于网络或其他原因导致数据库更新失败,就可能会导致缓存和数据库中的数据不一致.
    • 缓存过期:在缓存中存储的数据可能会因为时间到期或者缓存空间不足等原因被清除,但是数据库中的数据仍然存在,这时就会出现缓存和数据库中的数据不一致.
    • 缓存穿透:如果请求一个不存在的数据,就会导致缓存中没有该数据,但是每次请求都会访问数据库,这就会出现缓存和数据库中的数据不一致.

解决方案

  • 延迟双删方案:先删除缓存,等待一段时间后再更新数据库,可以通过缓存删除和数据库更新之间的时间差来保证数据一致性.这种方案的优点是实现简单,但是需要保证缓存删除和数据库更新之间的时间差不会太大,否则可能会出现数据不一致的情况.

    1
    2
    3
    4
    redis.delKey(X) 
    db.update(X)
    Thread.sleep(N)
    redis.delKey(X)
  • 先更新数据库再删除缓存方案:先更新数据库,然后再删除缓存,可以避免缓存删除失败和并发更新的问题.但是,如果在更新数据库和删除缓存之间出现网络异常或其他故障,可能会导致缓存和数据库不一致.

    • 优先使⽤先更新数据库再删除缓存的⽅法,原因主要有两个:
      1. 先删除缓存值再更新数据库,有可能导致请求因缓存缺失⽽访问数据库,给数据库带来压⼒;
      2. 如果业务应⽤中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间就不好设置。
  • 使用分布式锁方案:使用分布式锁来保证缓存删除和数据库更新的原子性,可以避免数据不一致的问题.但是,使用分布式锁会增加系统的复杂度和延迟.

  • 使用消息队列方案:将缓存删除和数据库更新的操作放入消息队列中,然后按照顺序依次执行,可以避免数据不一致的问题.但是,使用消息队列会增加系统的复杂度和延迟.

读写缓存处理"数据不一致"方法

  • 同步写回策略: 写缓存时,也同步写数据库,缓存和数据库中的数据⼀致;

只读缓存处理"数据不一致"方法

操作顺序 是否有并发请求 潜在问题 现象 应对方案
先删除缓存值,再更新数据库 缓存删除成功,但数据库更新失败 应用从数据库读到旧数据 重试数据库更新
缓存删除后,尚未更新,有并发读请求 并发请求从数据库读到旧值,并且更新到缓存,导致后续请求都读取旧值 延迟双删
先更新数据库,再删除缓存 数据库更新成功,但缓存删除失败 应用从缓存读到旧数据 重试缓存删除
数据库更新成功后,尚未删除缓存,有并发读请求 并发请求从缓存中读到旧值 等待缓存删除完成,期间会有不一致数据短暂存在

具体操作步骤

实现缓存与数据库双写一致

  • 查询逻辑:
    1. 从缓存中查询数据缓存
    2. 判断缓存中是否存在,若缓存中存在,则直接返回
    3. 缓存中不存在,则根据数据唯一标识查询数据库
    4. 若查询数据库不存在,则返回错误信息
    5. 数据库中存在,写入缓存
    6. 返回查询到的数据
  • 更新逻辑:
    1. 先更新数据库
    2. 再删除缓存