参考:
1. 先更新数据库,再更新缓存
-
不可行。
-
原因分析:
-
频繁更新缓存浪费资源(写多读少的场景)
-
缓存数据计算复杂,浪费性能(可能涉及多张表的计算)
-
线程并发安全问题。(更新缓存的顺序不一致,导致脏数据)
-
2.先更新数据库,再删除缓存
旁路缓存策略(Cache Aside Pattern)
-
使用方式
-
基本读取: 先读缓存,缓存中没有数据的话,去数据库中读取,然后存入缓存中,同时返回响应。
-
先更新数据库,后删除缓存。
-
-
存在问题:
-
数据库更新成功,缓存删除失败。导致数据库中的数据是最新的,但缓存中的是旧数据。
-
并发问题
-
读请求去查询缓存时,缓存刚好失效。
-
读请求去查询数据库,得到旧值。
-
写请求将新值写入数据库,写请求删除缓存。
-
读请求将旧值写入缓存。
-
-
分析并发问题出现的概率
- 概率非常低,因为条件需要具备读缓存失效,而且并发一个写操作。读操作必须在写操作前进入数据库操作,而又要晚于写操作更新缓存。但实际上写操作比读操作慢得多,所以概率非常小。
-
-
改进:
-
提供一个保障的重试机制即可。目前有两种方案。
-
方案一:可以使用消息队列来保证缓存一致性。
-
流程:
-
更新数据库
-
缓存删除失败
-
将需要删除的key发送至消息队列
-
自己消费消息,获得需要删除的key。
-
继续重试删除操作,直到成功。
-
-
但这种方式借助了消息中间件,增加了复杂度,对业务代码造成大量侵入。对于一些一致性要求没那么高的场景,就显得没必要了。
-
-
方案二:启动一个订阅程序去订阅数据库的
binlog
,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。
-
3.先删缓存,再更新数据库
先删除缓存,后更新数据库。
-
假设缓存删除成功,但数据库更新失败。此时新的请求过来,就去数据库中读取旧数据更新到缓存中。
-
并发度很低时适用。
缓存延时双删策略
-
先删除缓存,后更新数据库方案存在的问题分析:
-
同时进入两个请求,一个写请求,一个读请求。
-
写请求先删除
redis
中的数据,然后去数据库进行更新操作。 -
读请求判断
redis
中有没有数据,没有数据时去请求数据库,拿到数据后写入缓存。(此时写请求还未更新DB成功,故读请求拿到的是旧数据) -
写请求更新DB成功后,出现缓存与数据库不一致问题。
-
使用了MySQL的读写分离架构,造成双写不一致的原因:
-
同时进入两个请求,一个写请求,一个读请求。
-
写请求删除缓存,更新成功主库的数据。但还没同步到从库。
-
读请求判断
redis
中有没有数据,没有数据时去请求从库,拿到数据后写入缓存。(此时拿到的是旧值) -
数据库完成主从同步,从库变为新值。
-
-
解决办法:延时双删策略
-
先删除缓存
-
再写数据库
-
异步等待一段时间后,再次淘汰缓存。(这里的时间设定主要是保证读请求结束,写请求可以删除读请求遭成的缓存脏数据,需要自行评估确定。)
-
-
该方案解决了高并发情况下,同时有读请求与写请求时导致的不一致问题。读取速度快,但是可能会出现短时间的脏数据。
-
如果第二次删除也删除失败,则需要添加重试机制保证一定删除成功。