2. 缓存一致性定义:强一致性、弱一致性、最终一致性、读写一致性模型
在分布式缓存与数据库构成的混合存储体系中,“一致性”不再是一个单一维度的概念。不同业务场景对数据在缓存与后端存储之间同步的即时性、顺序性和可观测性有着截然不同的要求。本章从最严格的强一致性出发,逐步过渡到弱一致性与最终一致性,并重点剖析读写一致性模型——这是构建缓存闭环架构时最容易引发线上故障的根源。
2.1 一致性模型全景对比
下表从核心定义、典型实现、适用场景和容错代价四个维度,对四种主流一致性模型进行横向对比:
| 模型 | 核心定义 | 典型实现 | 适用场景 | 容错代价 |
|---|---|---|---|---|
| 强一致性 | 写入完成后,任何后续读取都能立即读到最新值 | 同步双写、分布式锁、两阶段提交 | 金融交易、库存扣减、配置中心 | 高延迟、低吞吐、可用性下降 |
| 弱一致性 | 写入后不保证立即读到最新值,只保证最终会收敛 | 异步写回、Cache-Aside 无锁模式 | 用户头像、日志聚合、非关键计数 | 低延迟、高吞吐、可容忍短暂不一致 |
| 最终一致性 | 在没有新写入的条件下,经过足够长时间后所有副本最终一致 | DNS 缓存、CDN 边缘节点、Gossip 协议 | 内容分发、社交动态、推荐系统 | 延迟窗口可控、最终收敛 |
| 读写一致性 | 写入者自己总能读到最新值,其他用户可能读到旧值 | Session 级缓存、写后读校验、本地时间戳 | 用户资料编辑、购物车、表单提交 | 仅保证写者自身视角,实现成本低 |
架构决策提示: 在缓存一致性闭环设计中,读写一致性 是最容易被忽视但影响面最大的模型。用户编辑完资料后刷新页面看到旧数据,往往不是缓存失效,而是读写一致性未被满足。
2.2 强一致性:同步屏障与线性化
强一致性要求系统对外表现为“单副本”行为——任何时刻、任何节点发起的读取操作,都必须返回最近一次写入的结果。在缓存场景中,这意味着缓存与数据库之间必须维持严格的同步屏障。
核心约束:
- 写入操作必须等待缓存和数据库都确认后才返回成功。
- 读取操作必须从最新副本(通常是主库或同步缓存)获取数据。
- 分布式环境下需要引入分布式锁或 Paxos/Raft 协调写入顺序。
强一致性的典型实现路径:
- 同步双写(Write-Through): 应用层同时写入缓存和数据库,任一失败则回滚整个操作。
- 分布式锁 + 缓存失效: 写入前获取全局锁,写入后主动失效缓存,后续读取强制回源。
- 两阶段提交(2PC): 在缓存和数据库之间引入协调者,保证原子提交。
// 强一致性写入伪代码(同步双写)
boolean writeWithStrongConsistency(String key, Object value) {
boolean dbSuccess = database.set(key, value);
if (!dbSuccess) return false;
boolean cacheSuccess = cache.set(key, value);
if (!cacheSuccess) {
// 缓存写入失败,回滚数据库
database.delete(key);
return false;
}
return true;
}
代价警示: 强一致性在分布式缓存中会显著降低可用性。当缓存节点故障时,写入操作必须等待超时或降级,容易引发连锁雪崩。实际工程中,99% 的缓存场景不需要全局强一致性。
2.3 弱一致性与最终一致性:异步收敛
弱一致性放弃“立即可见”的承诺,只保证数据最终会收敛到一致状态。最终一致性是弱一致性的一个特例——它额外承诺在没有新写入的情况下,经过一个有限的“不一致窗口”后,所有副本将达成一致。
最终一致性的关键参数:
- 不一致窗口(Inconsistency Window): 从写入完成到所有副本读取到最新值的时间差。
- 收敛速度: 取决于异步复制频率、网络延迟和冲突解决策略。
- 冲突处理: 最后写入胜利(LWW)、向量时钟、CRDT 等。
工程实践: 在 Cache-Aside 模式中,最终一致性通过“先更新数据库,再失效缓存”实现。不一致窗口通常为缓存失效到下次回源的时间间隔,典型值为 1~10 毫秒。
// 最终一致性写入伪代码(Cache-Aside 模式)
void writeWithEventualConsistency(String key, Object value) {
database.set(key, value); // 1. 先更新数据库
cache.delete(key); // 2. 再失效缓存(非删除,而是标记失效)
// 后续读取会 miss 缓存,回源数据库并重建缓存
}
2.4 读写一致性模型:写者自见
读写一致性(Read-Your-Writes Consistency)是一种比最终一致性更强、比强一致性更弱的折中模型。它保证:写入者自己 在写入完成后发起的读取操作,一定能看到自己刚写入的数据;但其他用户或节点可能仍然读到旧值。
这一模型在缓存架构中极其重要,因为用户对“自己操作的结果”有最直接的感知预期。
实现读写一致性的三种常见策略:
| 策略 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| Session 级缓存 | 为每个用户 Session 维护一份私有缓存,写入后立即更新 Session 缓存 | 实现简单,隔离性好 | Session 内存占用高,跨 Session 不共享 |
| 写后读校验 | 写入后立即从缓存读取一次,若不一致则强制回源 | 通用性强,不依赖 Session | 增加一次额外读取开销 |
| 本地时间戳 | 写入时记录本地时间戳,读取时携带时间戳,缓存返回不早于该时间戳的数据 | 精确控制可见性 | 需要缓存支持时间戳比较 |
架构建议: 在用户资料编辑、购物车修改、表单提交等场景中,优先采用 Session 级缓存 + 写后读校验 的组合方案。前者保证写者自见,后者兜底异常情况。
2.5 一致性模型选择决策树
下图展示了在缓存一致性闭环架构中,如何根据业务需求选择合适的一致性模型:
2.6 一致性模型在缓存闭环中的映射
在《错误处理缓存一致性闭环架构》中,不同一致性模型对应不同的错误处理策略和闭环反馈机制:
- 强一致性闭环: 写入失败时触发完整回滚(缓存 + 数据库),闭环检测周期短(毫秒级),错误处理成本高。
- 读写一致性闭环: 写入失败时仅回滚写者自己的缓存视图,其他用户不受影响;闭环检测通过写后读校验触发。
- 最终一致性闭环: 写入失败时依赖异步补偿任务(如定时对账)修复不一致,闭环检测周期长(秒级到分钟级)。
- 弱一致性闭环: 几乎不设主动检测,依赖业务层幂等或用户刷新触发修复。
核心结论: 缓存一致性不是“全有或全无”的选择。在同一个系统中,不同数据可以共存不同的一致性模型。例如:库存数据使用强一致性,用户昵称使用最终一致性,购物车使用读写一致性。这种 混合一致性架构 是缓存闭环设计的核心实践。