1. 缓存一致性概述
1.1 为什么需要缓存
在分布式系统中,缓存是提升数据读取性能、降低数据库负载的核心手段。当大量请求直接穿透到数据库时,磁盘 I/O 和连接池会成为瓶颈,导致响应延迟飙升甚至服务雪崩。引入缓存层后,热点数据可以直接从内存返回,将读延迟从毫秒级降至微秒级。
核心收益: 读吞吐量提升 10~100 倍、数据库连接数减少 60%~80%、尾部延迟(P99)显著降低。
典型应用场景包括:
- 热点商品详情页:秒杀/大促期间,商品信息几乎全部命中缓存。
- 用户会话状态:Session 数据从关系库迁移到 Redis,避免频繁建连。
- 配置/规则中心:业务规则、黑白名单等低频变更数据,缓存后减少 DB 查询。
1.2 缓存与数据库的差异
缓存系统(如 Redis、Memcached)与关系型数据库(如 MySQL、PostgreSQL)在底层设计上存在本质区别,这些差异直接决定了它们无法天然保持数据一致。
| 对比维度 | 缓存(Cache) | 数据库(DB) |
|---|---|---|
| 存储介质 | 内存(DRAM)为主,部分支持持久化 | 磁盘(SSD/HDD)为主,内存为辅助 |
| 数据模型 | Key-Value、Hash、List 等简单结构 | 关系表、索引、事务、约束 |
| 一致性保证 | 最终一致性为主,无强事务 | ACID 事务,支持强一致性 |
| 过期策略 | TTL 自动淘汰,LRU/LFU 驱逐 | 数据持久存在,无自动过期 |
| 写入路径 | 通常只写内存,异步刷盘 | WAL + 数据页,同步/半同步 |
| 典型延迟 | 0.1~1 ms | 1~20 ms(含网络与磁盘) |
关键认知: 缓存不是数据库的副本,而是加速层。两者在一致性模型上的差异,是缓存一致性问题的根本原因。
1.3 一致性问题产生的根源
当应用程序同时操作缓存和数据库时,由于写入顺序、并发冲突、网络分区等因素,缓存中的值与数据库中的真实值可能产生偏差。这种偏差就是缓存一致性问题。
问题根源可归纳为以下三类:
- 双写时序不一致:更新数据库后,缓存更新失败或延迟,导致旧数据被后续请求读取。
- 并发覆盖:多个线程/节点同时读写同一 Key,后写入的缓存可能覆盖了正确的数据库值。
- 缓存淘汰与回填竞争:缓存过期后被淘汰,回填时数据库数据已被其他事务修改,导致回填了旧值。
1.4 一致性问题的典型表现
在实际生产环境中,缓存一致性问题会以多种形态暴露出来,测试人员需要能够识别并分类这些异常现象。
| 问题类型 | 现象描述 | 影响等级 |
|---|---|---|
| 脏读 | 读取到缓存中尚未提交或已回滚的旧数据 | 严重 |
| 缓存穿透 | 大量请求绕过缓存直接打到数据库,导致 DB 压力激增 | 严重 |
| 缓存雪崩 | 大量缓存同时过期,请求全部穿透至 DB,引发连锁故障 | 灾难 |
| 数据回滚不一致 | 数据库事务回滚后,缓存中已写入的数据未被清除 | 中等 |
| 主从延迟导致脏缓存 | 写入主库后,从库尚未同步,缓存回填了从库的旧值 | 中等 |
测试关注点: 缓存一致性测试不能只关注功能正确性,还需要覆盖并发、网络抖动、节点宕机、主从切换等异常场景。后续章节将逐一展开对应的测试设计与验证方法。
1.5 一致性模型分类
在讨论缓存一致性之前,需要明确不同的一致性模型定义。分布式系统中常见的一致性模型如下:
- 强一致性(Linearizability):写入完成后,任何后续读取都能立即看到最新值。实现代价高,通常需要分布式共识协议(如 Paxos/Raft)。
- 最终一致性(Eventual Consistency):在没有新写入的情况下,经过足够长的时间,所有副本最终会收敛到相同的值。缓存系统默认采用此模型。
- 因果一致性(Causal Consistency):有因果关系的操作按顺序可见,无因果关系的操作可以并发。
- 读己之写(Read-Your-Writes):客户端写入后,总能读到自己的写入,但其他客户端可能暂时看不到。
课程定位: 本课程聚焦于最终一致性模型下的缓存与数据库一致性测试。强一致性场景通常依赖分布式事务或共识算法,不在本课程讨论范围内。