1. 缓存一致性概述
1.1 缓存一致性问题的定义
在分布式系统中,缓存一致性(Cache Consistency)是指缓存层与数据源(如数据库、远程服务)之间数据状态的对等程度。当缓存中存储的数据与源端数据在任何时刻都保持相同,则称系统达到了强缓存一致性;若允许短暂或最终对齐,则属于最终一致性模型。
缓存一致性问题的本质是:同一份数据在多个存储副本(缓存副本 + 数据库副本)之间出现了值分歧。这种分歧一旦被业务请求读取到,就会导致脏读、重复提交、库存超卖、配置错乱等严重故障。
核心定义:缓存一致性 = 缓存中的数据与源端数据在业务可接受的时间窗口内保持逻辑等价。
1.2 产生原因:写后读与删后读
缓存一致性问题主要由两类操作时序冲突引发:写后读(Write-Then-Read) 和 删后读(Delete-Then-Read)。下表对比了两种场景的触发条件与典型后果:
| 冲突类型 | 操作时序 | 典型场景 | 后果 |
|---|---|---|---|
| 写后读 | 线程A更新数据库 → 线程B读取缓存(旧值) | 用户修改个人信息后刷新页面仍看到旧资料 | 脏读、数据不一致 |
| 删后读 | 线程A删除缓存 → 线程B并发写入旧缓存 → 线程C读取到旧值 | 商品库存扣减后仍显示原库存 | 缓存与数据库长期不一致 |
写后读(Write-Then-Read)
写后读是最常见的一致性破坏路径。流程如下:
- 业务线程先更新数据库(写操作)。
- 缓存未及时失效或更新。
- 另一个线程读取缓存,拿到的是更新前的旧数据。
这种问题在先写库、后删缓存的策略中尤为突出——如果删除缓存失败或延迟,后续读请求就会命中旧缓存。
删后读(Delete-Then-Read)
删后读的破坏性更强,因为它涉及并发写入的竞态条件:
- 线程A删除缓存键。
- 线程B(读请求)发现缓存缺失,回源数据库读取旧数据,并将旧数据写回缓存。
- 线程A此时才更新数据库(或更新操作在步骤1之前已完成但尚未生效)。
- 缓存中永久保留了旧数据,数据库已更新,二者长期不一致。
注意:删后读问题在高并发场景下极易触发,是缓存穿透保护失效的常见诱因之一。即使采用“延迟双删”策略,也无法完全消除时间窗口内的不一致风险。
1.3 对系统稳定性的影响
缓存一致性问题的破坏力不仅体现在数据层面,还会逐级放大为系统稳定性风险。以下是主要影响维度:
| 影响维度 | 具体表现 | 严重等级 |
|---|---|---|
| 数据正确性 | 用户看到过期数据、库存超卖、订单状态错乱 | P0(致命) |
| 缓存穿透放大 | 不一致导致缓存频繁失效,大量请求穿透到数据库 | P1(严重) |
| 数据库负载飙升 | 穿透流量打垮数据库,引发级联故障 | P1(严重) |
| 业务逻辑紊乱 | 基于缓存做判断的限流、降级、风控策略失效 | P2(中等) |
| 运维排障困难 | 不一致问题间歇性复现,难以定位根因 | P2(中等) |
经验法则:缓存一致性问题的治理优先级应高于缓存命中率优化。一个命中率99%但存在数据不一致的缓存系统,比一个命中率90%但数据绝对一致的系统更危险。
1.4 一致性模型分类
根据业务对一致性的容忍度,缓存一致性模型可分为以下三类:
- 强一致性(Strong Consistency):写操作完成后,任何后续读操作都能读到最新值。实现代价高,通常需要分布式锁或同步失效,适用于金融、库存等场景。
- 最终一致性(Eventual Consistency):允许短暂的不一致窗口,但保证在无新写入后,所有副本最终达到一致。适用于社交动态、内容推荐等场景。
- 弱一致性(Weak Consistency):不保证读操作能拿到最新值,甚至不保证最终一致。适用于日志、监控等非关键数据。
在实际工程中,大多数业务系统采用最终一致性 + 补偿机制的组合策略,在性能与正确性之间取得平衡。
关键认知:缓存一致性不是“全有或全无”的二元选择,而是一个可调优的连续谱。通过合理的OpenAPI契约设计,可以在不同接口上声明不同的一致性等级,实现精细化的治理。