如何正确失效Redis缓存以保证数据一致性?
缓存失效竞态问题的解决方案
你遇到的是典型的缓存写回竞态,核心是读操作的"查库-写缓存"流程和写操作的"更新库-删缓存"流程并发执行,导致旧数据被写入缓存。下面是几种实用的解决思路:
1. 版本号校验机制
给每条数据加一个版本号,数据库和缓存中都存储版本信息:
- 读取流程:
- 缓存命中直接返回;缓存未命中时,查询数据库获取数据和当前版本号V
- 准备写入缓存前,再次查询数据库的版本号,如果还是V,才将数据和版本号写入缓存;如果版本号已更新,直接返回最新数据,放弃写缓存
- 更新流程:
- 更新数据库数据的同时,将版本号递增
- 可以选择直接写入带新版本号的缓存,或者删除缓存(读取时会自动校验版本并拉取最新数据)
这种方法能从根源上避免旧数据写入,适合对数据一致性要求高的业务场景。
2. 分布式锁控制
在读取缓存未命中后,先获取对应数据的分布式锁(比如按数据ID加锁):
- 拿到锁的客户端才能执行"查库-写缓存"操作,执行完成后释放锁
- 没拿到锁的客户端可以选择等待锁释放后重新读缓存,或者直接查询数据库返回
注意锁的粒度要尽可能细,超时时间设置合理,避免影响并发性能或出现死锁。适合读写频率不算极高的场景。
3. 延迟删除缓存
更新数据库后不立即删除缓存,而是延迟1~5秒再执行删除操作:
- 延迟时间根据业务的平均"查库-写缓存"耗时调整,确保旧的写缓存操作完成后,再把旧缓存删掉
- 下次读取时就会拉取最新的数据库数据写入缓存
这种方法实现最简单,不需要额外组件,适合对一致性要求不是极端严格的场景,比如商品列表、非实时统计数据等。
4. 直接更新缓存而非删除
把"更新库-删缓存"改成"更新库-写缓存":
- 更新数据库后,直接将最新数据写入缓存,覆盖掉旧值
- 配合缓存的CAS(Compare And Swap)操作,只有当缓存中的数据版本低于数据库版本时才执行更新,进一步避免并发冲突
这种方法能减少缓存未命中的次数,但要注意如果更新操作失败时,需要有回滚机制,避免缓存和数据库不一致。
内容的提问来源于stack exchange,提问作者Zanko
相关产品推荐
相关产品推荐

