回答
一般来说,Redis 和数据库的双写方案有如下四种:
- 先更新缓存,再更新数据库
- 先更新数据库,再更新缓存
- 先删除缓存,再更新数据库
- 先更新数据库,再删除缓存
由于更新缓存比删除缓存更加复杂且容易产生数据不一致的情况,所以推荐采用删除缓存策略。针对删除缓存的方案,我们可以基于如下策略做选择:
- 业务量不大,并发不高,推荐选择先更新缓存再删除缓存,因为这种方案相对会简单些,所面对的场景也简单些,并发量不高,加上删除缓存失败原本就是一个小概率事件,所以对删除缓存失败的情况的容错性就更大。
- 如果是在高并发的场景下,推荐采用先删除缓存,然后再更新数据库,原因有两个:
- 先更新数据库,再删除缓存:中间存在数据不一致的窗口期,高并发场景下,会放大这个缺陷。
- 针对“读写并发”情况导致数据的不一致性:首先这种情况是一个非常小概率事件,因为读操作是一个非常快的情况,而在这个期间恰好遇到一个写操作耗时的场景,这种概率就会更小了。而且我们可以使用“延迟双删策略”、“分布式锁“等策略让这种方案近乎完美。
三种方案保证数据库与缓存的一致性
延迟双删策略
- 第一次删除缓存:在更新数据库之前先删除缓存中的旧数据。
- 更新数据库
- 延迟一定时间:等待一段时间,确保所有的旧数据读请求都已经穿透到数据库中。
- 第二次删除缓存:再次删除缓存,保所有在数据库更新期间由于缓存失效而从数据库中读取并重新写入缓存的旧数据被清除。
删除缓存重试机制
将第二次删除缓存的数据存储到消息队列中(如 Kafka),由消费者来执行删除操作。
- 如果应用程序第二次删除缓存失败,则从消息队列中重新读取数据,再次删除缓存,这就是重试机制。但是我们需要限定一个重试的次数,如果超过这个限定次数,我们可以报个警。
- 如果缓存删除成功,就把数据从消息队列中删除,避免重复操作。
订阅 binlog 异步删除缓存
数据库更新记录成功后会产生一条变更日志记录在 binlog
,我们可以订阅 binlog
日志,拿到具体的操作数据,然后再执行删除缓存。
扩展
缓存的三种经典更新策略
一般我们有三种使用缓存的经典模式,每种模式对应不同的数据同步策略: