3. 数据库缓存一致性:缓存与数据库双写一致性策略

在数据库与缓存并存的架构中,保证两者数据一致是核心挑战。本章聚焦三种主流双写一致性策略:Cache-Aside、Read-Through 和 Write-Through,分析其工作原理、适用场景及在契约测试中的校验要点。

3.1 一致性问题的根源

缓存与数据库是两套独立的存储系统,写入顺序、并发操作、网络延迟都可能导致数据不一致。常见问题包括:

  • 脏读:缓存中残留旧数据,数据库已更新。
  • 缓存穿透:大量请求绕过缓存直接访问数据库。
  • 缓存雪崩:缓存大面积失效,数据库压力骤增。
  • 并发覆盖:多个线程同时写入,导致数据错乱。
契约测试视角:在回归冒烟阶段,缓存API的响应格式必须明确标识数据来源(缓存/数据库)和时间戳,以便校验一致性策略是否生效。

3.2 Cache-Aside 策略

Cache-Aside(旁路缓存)是最常用的策略,应用程序直接管理缓存与数据库的读写操作。

3.2.1 读操作流程

  1. 应用先查询缓存。
  2. 缓存命中,直接返回数据。
  3. 缓存未命中,查询数据库。
  4. 将数据库结果写入缓存,并设置过期时间。
  5. 返回数据给调用方。

3.2.2 写操作流程

  1. 应用更新数据库。
  2. 删除或更新缓存中的对应数据。
常见陷阱:先更新数据库再删除缓存,存在短暂的不一致窗口。推荐使用“延迟双删”策略:先删除缓存,再更新数据库,最后延迟一段时间再次删除缓存。

3.3 Read/Write Through 策略

Read/Write Through(读写穿透)将缓存层作为数据访问的唯一入口,应用不直接操作数据库。

3.3.1 Read-Through 流程

  • 应用只与缓存交互。
  • 缓存未命中时,缓存组件自动从数据库加载数据。
  • 加载完成后,缓存返回数据给应用。

3.3.2 Write-Through 流程

  • 应用写入数据到缓存。
  • 缓存组件同步将数据写入数据库。
  • 写入成功后,缓存返回确认给应用。
关键区别:Cache-Aside 由应用控制一致性逻辑,灵活性高;Read/Write Through 由缓存层封装一致性逻辑,对应用透明,但实现复杂度转移到缓存中间件。

3.4 策略对比表

特性 Cache-Aside Read-Through Write-Through
应用复杂度 高(需自行管理缓存逻辑) 低(缓存层处理读) 低(缓存层处理写)
一致性保证 弱(需额外机制) 中等(读路径一致) 强(写路径同步)
性能影响 读快,写有缓存删除开销 首次读慢,后续快 写延迟增加(同步写库)
适用场景 读多写少,可接受短暂不一致 读密集,数据加载模式固定 写密集,要求强一致性
契约测试关注点 缓存删除后响应格式 缓存未命中时回源响应 写入后缓存与数据库同步状态

3.5 双写一致性流程对比图

下图展示了 Cache-Aside 与 Write-Through 在写操作时的核心差异:

Cache-Aside vs Write-Through 写操作流程对比 Cache-Aside 写流程 1. 应用更新数据库 2. 删除缓存数据 3. 延迟后再次删除缓存 4. 返回写入成功 Write-Through 写流程 1. 应用写入缓存 2. 缓存同步写入数据库 3. 数据库确认写入成功 4. 缓存返回写入成功 Cache-Aside 采用“先更新库再删缓存+延迟双删”;Write-Through 由缓存层同步写库,保证强一致性

3.6 契约测试中的一致性校验

在回归冒烟测试中,缓存API的响应格式必须包含以下字段以支持一致性校验:

{
  "status": "success",
  "data": {
    "id": "user_123",
    "name": "张三",
    "updated_at": "2025-03-21T10:30:00Z"
  },
  "meta": {
    "source": "cache",        // 数据来源:cache / database
    "cache_hit": true,        // 是否缓存命中
    "consistency_strategy": "cache-aside",  // 当前策略
    "ttl_remaining": 120      // 缓存剩余生存时间(秒)
  }
}
契约断言要点:
  • 写操作后,缓存API应返回 source: "database" 或明确标识缓存已失效。
  • 读操作时,若缓存命中,cache_hit 必须为 true,且 source 为 "cache"。
  • Write-Through 策略下,写操作响应中应包含 db_write_confirmed: true 字段。

3.7 策略选择建议

业务场景 推荐策略 理由
用户信息读取(读多写少) Cache-Aside 灵活,可接受短暂不一致
商品库存(写密集) Write-Through 需要强一致性,避免超卖
配置信息(读密集,更新少) Read-Through 缓存层自动回源,应用无感知
会话数据(读写均衡) Cache-Aside + 延迟双删 平衡一致性与性能
回归冒烟最佳实践:在契约测试中,为每种策略定义独立的响应格式契约。例如,Cache-Aside 的写响应应包含 cache_invalidated: true,而 Write-Through 的写响应应包含 db_sync_status: "synced"。这样在回归测试中,可以快速定位一致性策略是否被正确执行。