平时吹牛或者面试时候尝尝会被问这个问题,今天尝试回答并记录一下
问:如何保证缓存和数据库数据一致性?或者问如何保证Redis和MySQL的数据一致性?
有几种方式:
- 只操作缓存,同步对数据库操作交给缓存中间件工具
- 同样只操作缓存,但是对数据库的操作异步交给缓存中间件(对数据一致性要求不高的可以用)
- 在一个事务中先更新数据库,成功后更新缓存
- 缓存延迟双删
- 缓存三删+数据一致性校验+强制读Redis主节点
主要就是以上五种,有些是类似的下面一个个详细说一下。
只读缓存,同步对数据库操作交给缓存中间件
这种很好理解,所有的业务系统的操作只针对缓存(Redis),缓存中有单独的任务(服务)去操作数据库,对数据库的增删改,但是这个需要同步进行,如果对数据库操作失败了,缓存也要回滚内容,从网上找到两张图,理解一下这种方式:
同步读:

同步写:

类似上一种,只读缓存,异步对数据库操作交给中间件
整体都比较类似,但是写的最后缓存对数据库的一步是交给异步了,不会同步进行,这种会有一定时间的缓存和数据库的数据不一致,所以对数据一致性要求比较高的就可以放弃这个方案了。
紧接着,就是:一个事务中先更新数据库,成功后更新缓存
这种是项目中比较常用的一种简单的实现方式,一个大事务中,先去更新数据库,当操作成功了,再去更新缓存。
比较好理解,有多个对数据库的操作,如果靠后的一个操作失败了,可以进行回滚,前面的数据库操作都回滚了,这时候没有操作缓存,所以数据是一致的。
如果先更新缓存,再操作数据库,虽然在一个事务,但是Redis并不受项目中事务影响,别的事务去读数据时候是最新的,所以当缓存更新数据库没更新时候,必然会出现数据不一致问题。
但是这种还有个比较大的问题,普通的项目还好,简单还好用,如果操作量级比较大,高并发情况下,众多大事务存在会造成死锁问题。
然后,缓存延迟双删
这个方案我也看了几遍才清楚
首先,为啥是删除缓存不是像上面的更新?
- 性能问题:更新缓存,如果涉及多表操作,删除比更新性能好,最多增加一次cache miss
- 安全问题:高并发场景下,会造成查询到的数据是旧值
其次就是具体介绍一下延迟双删了
- 删除缓存
- 写数据库
- 延迟队列,再删缓存
- 删除失败,重试机制(最终不行,人工介入)
延迟队列的延迟多久?这个需要根据具体情况去分析,太久了可能会造成大段时间的脏数据。延迟的目的就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。
双删主要目的是清除脏数据,一切从头再来
最后一个:缓存三删+数据一致性校验+强制读Redis主节点
这个其实是上一个的加强版,应该算是延迟双删的比较具体的实践方式,首先上图:

我也是从别人的帖子中找的这张图,不过。。。水印中名字和帖子主人不一样,估计也是借来的图
图片说的挺详细的,简单概括:
- 更新数据库,同步删除缓存
- binlog获取更新事件后发消息队列删除,并放入延迟队列
- 延迟队列数据一致性校验,不正确就脏数据修复,然后缓存删除
然后,这种方式的最后一句是强制读Redis主节点?
因为Redis也要集群的,也要主从副本的,也要主从延迟的。如果写入Redis后马上查询会因为延迟导致数据问题,Redis压力没有MySQL那么大,16384个槽分摊出去,都在不同机器上,所以强制读主时候压力没那么大,同样再来一张图片看看:

大概就是这样了,本来是想自己答一下,然后对比一下,后来索性就自己列个提纲,然后汇总一波了