缓存和数据库的一致性问题

从本质上讲,无论是先写数据库还是先写缓存,都是为了保证数据库和缓存的数据一致。数据库和缓存(redis)双写数据一致性问题,在高并发的场景下,这个问题尤为严重。

首先:只要是双写,总会有数据一致性的问题,大多数情况下我们并不是严格要求缓存+数据库 必须保持一致性。

简单数据获取流程:

image-20230910171044918

缓存删除而不是更新

其实删除缓存,而不是更新缓存,就是一个 lazy 计算的思想,不要每次都重新做复杂的计算,不管它会不会用到,而是让它到需要被使用的时候再重新计算。

image-20230910171110623

常见的四种方案

  1. 先更新缓存,再更新数据库
  2. 先更新数据库,再更新缓存
  3. 先删缓存,再更新数据库
  4. 先更新数据库,再删缓存

先更新缓存,再更新数据库

image-20230910171155451

此时缓存和数据库中的数据出现不一致,缓存中的数据也就没有了意义。

先更新数据库,再更新缓存

image-20230910171212873

数据库是新数据,而缓存是旧数据或者没有数据,出现数据不一致的情况。

image-20230910171227401

步骤说明(图中顺序为请求顺序):

  1. 请求1:进行到步骤2时出现问题,此时发生了延迟
  2. 请求2:步骤3、4,顺利执行
  3. 请求1:步骤2此时才开始执行。
  4. 请求3:获取缓存内的数据,此时获取到的是请求1写入缓存的数据,此时读取的是旧数据。

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

  • 数据库更新失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致
  • 读的时候缓存没有,所以去读了数据库中的旧数据,然后更新到缓存中。

image-20230910171317000

推荐:先更新数据库,再删除缓存(删除重试)

先更新数据库,再删除缓存。如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致。

image-20230910171256573

此时可以添加重试删除操作,直到成功(对业务线代码造成大量的侵入)。

推荐:先更新数据库,再删除缓存(延时双删)

依旧是先更新数据库,再删除缓存,一段时间间隔后再一次删除缓存。

image-20230910171339495

方案一:

  1. 更新数据库数据

  2. 数据库将数据表数据的变更信息写入binlog日志当中

  3. 订阅程序获取所需要的数据以及key

  4. 程序逻辑中处理具体的业务逻辑,接收订阅binlog、发起删除缓存的请求。

  5. 尝试删除缓存操作,发现删除失败

  6. 将这些信息发送至消息队列

  7. 重新从消息队列中获得该数据,重试操作。

其他:

  • 使用 DelayQueue,会随着 JVM 进程的死亡,丢失更新的风险
  • 使用 MQ
  • 缓存设置有效期
  • 订阅 Mysql 数据库的 binlog 日志对缓存进行操作
Last Updated:
Contributors: 拔土豆的程序员