1. 先更新数据库,再更新缓存
问题:(1)线程A写操作先更新数据库
(2) 线程B写操作也更新数据库
(3)当线程B比线程A先更新缓存(线程A停滞卡顿)
(4)线程A最后更新缓存
a,导致redis缓存与数据库不一致,出现脏数据
b,频繁更新,浪费性能
2.先删缓存,再更新数据库
1,A,B两个线程同时要更新数据,并且A,B已经都做完了删除缓存这一步,A先更新数据库,C线程读取数据,由于缓存没有,则查数据库,并把A更新的数据,写入了缓存,最后B更新数据库。那么缓存和数据库的值不一致
2, (1)请求A进行写操作,删除缓存
(2)请求B查询发现缓存不存在
(3)请求B去数据库查询得到旧值
(4)请求B将旧值写入缓存
(5)请求A将新值写入数据库
延时双删+设置超时时间
(1)先淘汰缓存
(2)再写数据库
(3)休眠1秒,再次淘汰缓存
伪代码:
public void write(String key,Object data){
redis.delKey(key);
db.updateData(data);
Thread.sleep(1000);
redis.delKey(key);
}
3.先更新数据库,再删除缓存
流程:
-
首先从缓存中查询数据,如果缓存命中则直接返回。
-
缓存未命中,则去数据库中读取。
-
将从数据库中读取的结果的副本放入到缓存中,并返回。
-
写操作
流程:
- 首先更新数据库。
- 然后删除缓存中的数据。
问题:(1)缓存刚好失效
(2)请求A查询数据库,得一个旧值
(3)请求B将新值写入数据库
(4)请求B删除缓存
(5)请求A将查到的旧值写入缓存
但 读操作比写操作耗时更少,上述情况出现概率极低
使用Cache-Aside Pattern时,一定要合理地设置过期策略。如果过期时间太短,可能导致大量请求涌入数据库。相反,如果过期时间太长,有可能导致缓存中数据的大量失效。使用缓存的一个原则,就是尽量缓存那些相对静态的、频繁被读取的数据。
推荐使用Cache Aside Pattern;
但需根据自身业务场景合理变通。
通过给缓存设置合理过期时间,是保证最终一致性的解决方案。
思考:若删除缓存失败导致数据不一致:
保障的重试机制:消息队列