浅析Redis、MySQL缓存双写不一致问题
前情提要
先做一个说明,从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。这种方案下,我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,那么后续的请求会从数据库拿取最新的数据。
在本文中主要讨论三种策略:
- 先更新数据库,再更新缓存
- 先删除缓存,再更新数据库
- 先删除缓存,再更新数据库,延迟1s,再删除缓存(延时双删)
先更新数据库,再更新缓存
对于这种方案,是大家都比较反对的,不采用理由如下:
线程安全问题
- 线程A更新了数据库
- 线程B更新了数据库
- 线程B更新了缓存
- 线程A更新了缓存
正常来讲A会比B先更新缓存,但由于网络波动等原因导致B先更新,这就导致了缓存为脏数据。如果没有设置缓存时间,那永远就是脏数据
业务场景角度
- 如果你是一个写数据库场景比较多,而读数据场景比较少的业务需求,采用这种方案就会导致,数据压根还没读到,缓存就被频繁的更新,浪费性能。
- 如果你写入数据库的值,并不是直接写入缓存的,而是要经过一系列复杂的计算再写入缓存。那么,每次写入数据库后,都再次计算写入缓存的值,无疑是浪费性能的。显然,删除缓存更为适合。
先删除缓存,再更新数据库
该方案会导致不一致原因是。同时一个请求A进行更新操作,另一个请求B进行查询操作。那么会出现如下情形:
- 请求A进行写操作,删除缓存
- 请求B查询发现没有缓存
- 请求B去数据库查询到旧数据
- 请求B将旧数据写入缓存
- 请求A将新值写入数据库
还是一样,如果没有设置缓存时间,那永远就是脏数据。
延迟双删除策略
操作步骤如下:
- 先删除缓存
- 修改更新数据库
- 休眠1s(具体时间由修改数据库业务的执行时间决定)
- 再次删除缓存(将修改数据库的时间内产生的脏数据删除)
在设置休眠时间时如果需要考虑到主库数据同步到从库的情况,那么再把延迟时间增长几百ms
当然,还有一种情况就是在第二次删除的时候删除失败怎么办,这样还会导致脏数据。
解决方案:设置重试机制,多删几次直到成功
- 更新数据库数据
- 由于各种原因导致删除缓存失败
- 将删除失败的key放入消息队列
- 自己消费消息,获取需要删除的key
- 重试删除操作,直到删除成功为止
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程