【大厂思维】缓存一致性问题解决方案
一、三种方案
- 延迟双删:缓存与数据库一致性问题的公认解决方案,也是面试高频考点,在各类技术八股文中均有重点提及。
- 租约机制:多见于分布式场景的大厂方案,更适配高并发、大规模架构。对于流量较小的内部 ERP 等系统,通常无需缓存设计,因此极少采用。
- 版本号对比机制:同样是大厂常用方案,核心通过数据自带的版本号标识,直观判断数据新旧,进而保障数据一致性。
二、延迟双删
2.1 什么是延迟双删
通过两次缓存删除操作,最大限度降低缓存与数据库的数据不一致风险
-
首次删除:更新数据库前,先删除缓存数据。
作用:避免更新时,请求命中缓存,读取到旧数据
-
数据库更新:更新数据库中的数据。
-
延迟后第二次删除:等待几秒(具体时间,根据业务需要调整)后,将缓存中的数据再次删除。
作用:解决首次删除后,数据库更新完成前的并发问题。即有并发请求,旧数据被重新写入缓存的问题
2.2 优缺点
优点:
- 实现成本低。
- 兼容性强,支持常见的缓存、数据库。
- 覆盖并发窗口,降低缓存与数据库的不一致性概率。
缺点:
- 可能存在缓存击穿的问题。
- 延迟删除时间难以把控。
- 依赖异步任务的可靠性。(一般情况下,延迟删除通过定时任务、消息队列实现,如发生故障,难以保证第二次删除)
- 无法满足强一致性的要求。
2.3 适用
- 中小型规模
- 并发量较小,数据库能承受短时的缓存击穿。
- 无复杂的分布式架构
三、租约机制
3.1 什么是租约机制
简单的说,就是多个请求查询缓存,只有第一个请求可以获取到令牌后写入缓存。
- 缓存中不存在查询的数据时,多个并发请求中,只给第一个颁发“令牌”。
- 拿到令牌后,才允许其查询数据库,写入缓存。其他请求,要么等待,要么放弃。
3.2 有缺点
优点:
- 防止并发出现混乱,不会出现旧数据覆盖新数据的问题。
- 轻量化,依靠Redis Lua脚本实现。
- 令牌过期时间可灵活设置。
缺点:
- 实现比延迟双删复杂,需要用Lua脚本实现令牌的生成、验证、删除,令牌过期、请求排队也需要考虑。
- 存在等待期,未获取到令牌的请求,要么等现有令牌过期,要么放弃,极端情况下部分用户可能需要等待。
- 缓存服务依赖度高,令牌的实现在缓存中,缓存一旦宕机,就可能出现租约机制失效的问题。
3.3 适用
- 大流量分布式系统
- 并发写多的业务
- 可接受等待的业务
四、版本号对比机制
4.1 什么是版本号对比机制
给每条数据,添加一个时间戳字段或版本号字段。
写缓存是,先判断版本号的新旧,新版本存入,旧版本丢弃。
4.2 优缺点
优点:
- 性能好,直接对比版本号,无需排队等待。
- 数据一致性,根据版本号,可防止旧数据覆盖新数据的问题。
- 实现较简单,无需考虑令牌的生成、过期等逻辑。
- 无锁设计,不会出现锁竞争、死锁等问题。
缺点:
- 版本号生成逻辑出现问题,会导致数据更新异常。
- 无法解决缓存击穿问题。
- 版本号存储占用空间。
4.3 适用
- 超大规模的高并发系统。
- 读多写少、数据更新频繁的业务。
- 需要“强一致性”优先级高于“防止缓存击穿”
五、总结
| 方案 | 适用 | 核心逻辑 | 优点 |
|---|---|---|---|
| 延迟双删 | 中小系统 | 删除缓存、延迟删除 | 简单 |
| 租约机制 | 大型系统 | 令牌的控制逻辑 | 防缓存击穿 |
| 版本号对比机制 | 超大型系统 | 版本号对比 | 无锁,性能好 |