1. 缓存一致性概述
1.1 什么是缓存一致性
缓存一致性(Cache Consistency)是指在分布式系统中,缓存层与数据库(或其他数据源)之间保持数据内容同步的程度。当业务数据在数据库中被更新后,缓存中对应的副本数据应当及时反映这一变化,否则就会出现“缓存与数据库数据不一致”的问题。
从技术实现角度看,缓存一致性包含两个核心维度:
- 时间维度:数据更新后,缓存多久能感知并同步最新值
- 空间维度:同一份数据在多个缓存节点或副本之间是否一致
核心公式: 缓存一致性 = 数据新鲜度 + 副本收敛速度 + 更新可见性
1.2 为什么需要缓存一致性治理
在互联网高并发架构中,缓存是提升系统吞吐量的核心手段。然而,缓存与数据库之间的数据不一致会引发严重的业务问题。缓存一致性治理的目标是在性能与一致性之间找到可接受的平衡点。
| 治理维度 | 业务影响 | 典型后果 |
|---|---|---|
| 数据准确性 | 用户看到过期或错误数据 | 订单状态显示错误、库存超卖 |
| 系统可靠性 | 缓存穿透/雪崩风险增加 | 数据库压力飙升、服务降级 |
| 运维复杂度 | 排查问题困难 | 需要人工清理缓存、回滚数据 |
| 业务连续性 | 关键链路中断 | 支付、库存、价格等核心功能异常 |
治理红线: 对于金融、交易、库存等强一致性场景,缓存一致性治理不是可选项,而是必须项。一次缓存不一致导致的超卖或错账,可能造成百万级资损。
1.3 常见缓存不一致场景
在实际生产环境中,缓存不一致问题通常由以下六类典型场景引发。理解这些场景是制定治理策略的前提。
1.4 场景深度分析
以下对最典型的两个场景进行代码级分析,帮助理解不一致产生的根本原因。
场景1:先写数据库,后删缓存(Cache-Aside 模式)
// 伪代码:先写DB后删缓存
public void updateOrder(Order order) {
// 1. 更新数据库
orderDao.update(order);
// 2. 删除缓存(可能失败)
cache.delete("order:" + order.getId());
}
不一致原因: 步骤2删除缓存时,如果Redis宕机、网络超时或删除命令被丢弃,缓存中仍保留旧数据。后续读请求会命中旧缓存,直到缓存过期。
场景2:先删缓存,后写数据库
// 伪代码:先删缓存后写DB
public void updateOrder(Order order) {
// 1. 删除缓存
cache.delete("order:" + order.getId());
// 2. 更新数据库(耗时较长)
orderDao.update(order);
// 3. 并发读请求在步骤1-2之间回填了旧数据
}
不一致原因: 在步骤1删除缓存后、步骤2更新数据库完成前,如果有并发读请求发现缓存为空,会从数据库读取旧数据并回填到缓存,导致缓存中永久存储旧值。
治理启示: 场景1和场景2揭示了缓存一致性治理的两个核心矛盾——更新顺序与并发控制。后续章节将围绕延迟双删、异步补偿、分布式锁等策略展开详细方案。
1.5 一致性治理的权衡模型
缓存一致性治理本质上是在以下三个目标之间寻找平衡:
| 目标 | 描述 | 典型策略 | 代价 |
|---|---|---|---|
| 强一致性 | 任何时刻缓存与DB完全一致 | 同步双写 + 分布式锁 | 性能下降50%-80% |
| 最终一致性 | 允许短暂不一致,最终收敛 | 异步删除 + 过期机制 | 存在秒级不一致窗口 |
| 弱一致性 | 容忍较长时间不一致 | 仅依赖TTL过期 | 不一致窗口可达分钟级 |
课程核心观点: 没有银弹式的缓存一致性方案。治理的关键在于:
1. 识别业务场景对一致性的真实要求
2. 选择合适的缓存更新策略(延迟双删、异步队列、Binlog监听等)
3. 调优连接池参数,降低不一致窗口的持续时间
1. 识别业务场景对一致性的真实要求
2. 选择合适的缓存更新策略(延迟双删、异步队列、Binlog监听等)
3. 调优连接池参数,降低不一致窗口的持续时间