第3章 强一致性方案设计:分布式锁 + 双写、写后立即失效、同步刷新机制

当业务对缓存与数据库的一致性要求达到“任何时候读取缓存都必须返回最新已提交数据”时,弱最终一致性方案不再适用。本章围绕分布式锁 + 双写、写后立即失效、同步刷新三种典型强一致手段展开,明确各自的应用边界、组件分层、数据流与控制流,并给出落地步骤与验收标准。

3.1 方案边界

  • 适用场景:金融交易、库存扣减、订单状态、账户余额等对数据准确性要求零容忍的业务。
  • 不适用的边界:高并发读多写少且允许短暂不一致的查询场景(如资讯列表、用户主页),此时强一致会带来不必要的锁开销与延迟。
  • 一致性等级:线性一致性(Linearizability)——所有操作实时可见且顺序一致,等同于单副本数据库的行为。

3.2 分层架构

三个方案共享统一的分层模型,但锁与失效策略的部署位置存在差异:

分层组件职责
接入层API Gateway / LB请求路由、限流、参数校验
业务服务层写服务 / 读服务执行双写、锁获取、缓存失效与刷新
分布式协调层Redis / Zookeeper / Etcd提供分布式锁(互斥、可重入、自动续期)
缓存层Redis Cluster / Memcached缓存键值存储,支持TTL与显式删除
持久层MySQL / PostgreSQL写入业务数据,作为权威数据源
组件职责洞察:分布式锁只在双写方案中必须前置获取;写后立即失效无需锁,但需要保证失效操作的原子性;同步刷新方案则依赖锁防止并发刷新。

3.3 数据流 / 控制流 / 信任链路

3.3.1 分布式锁 + 双写

  • 控制流:写服务先请求分布式锁 → 获取成功后写入数据库 → 写入缓存(双写) → 释放锁。
  • 数据流:业务数据从服务 → DB(持久化) → 缓存(冗余);在读请求未命中缓存时,读服务直接回查DB,但须等待锁释放。
  • 信任链路:锁服务担保写操作互斥,DB存储了最终数据,缓存只是热读副本。

3.3.2 写后立即失效

  • 控制流:写服务直接更新DB → 随即调用 cache.del(key) 删除缓存 → 业务结束。
  • 数据流:后续读请求必然缓存穿透,回查DB并重新加载最新数据写入缓存。
  • 信任链路:DB始终是最新,缓存只存在短暂缺失而非不一致。

3.3.3 同步刷新机制

  • 控制流:写服务先获取分布式锁 → 更新DB → 同步调用缓存更新(SET) → 释放锁。
  • 数据流:缓存数据与DB同时更新,锁保证了串行化写入,避免并发写入脏数据。
  • 信任链路:锁 + 原子性更新保证缓存与DB完全同步,但吞吐受限。
信任链路陷阱:任何写后失效或双写方案,若缓存删除操作因网络故障失败,将产生长期不一致。必须配合定期一致性校验或延迟双删作为补偿。
三种强一致方案流程对比 方案A:分布式锁+双写 ① 获取分布式锁 ② 写DB → 写缓存 ③ 释放锁 方案B:写后立即失效 ① 更新DB ② 删除缓存键 ③ 读穿透加载最新DB 方案C:同步刷新 ① 获取锁 ② 写DB ③ 写缓存(同步) ④ 释放锁 延迟关键点:方案A与C含锁获取&释放开销,方案B无锁但有缓存空洞期

3.4 选型权衡

评估维度分布式锁+双写写后立即失效同步刷新
一致性强度最强(线性)强(基本线性,依赖删除原子性)最强(线性)
写吞吐低(锁竞争)高(无锁)较低(锁+同步写缓存)
读延迟抖动低(缓存命中)高(频繁穿透)低(缓存始终热)
实现复杂度高(锁续期、防死锁)低中
故障场景风险锁超时导致双写不一致删除失败导致缓存残留旧数据锁失效能引起并发半写
推荐选型决策树:
  1. 能否接受缓存短暂缺失(数百ms)? → 能 → 选写后立即失效
  2. 写并发低于500ops且要求零数据冲突? → 选分布式锁+双写
  3. 读请求极频繁且写少,需缓存始终命中? → 选同步刷新(配合长TTL)

3.5 风险点与应对

  • 锁死锁 / 锁超时:锁持有时间超长导致服务雪崩。应对:自动续期 + 看门狗(如 Redis Client Redisson 的机制)。
  • 双写不一致:当写DB成功但写缓存失败时,缓存为旧数据。应对:重置缓存值并记录失败日志,由补偿Job对账修复。
  • 缓存删除失败:写后立即失效场景下,如果删除命令丢失,缓存永久陈旧。应对:延迟双删(先删缓存 → 写DB → 再删缓存),或引入版本号实现CAS。
  • 缓存雪崩:大量缓存在同一时刻失效(写后失效模式下)。应对:随机TTL或本地缓存降级。

3.6 落地步骤

  1. 识别热点键:通过监控按QPS排序,筛选需要强一致保障的缓存键。
  2. 引入分布式锁 SDK:选型 Redis Redlock / Etcd watch + lease,提供统一锁接口。
  3. 改造写服务:在业务事务单元内嵌入双写或刷新逻辑,确保锁范围包裹全部写入动作。
  4. 设计缓存失效补偿:对每次删除操作记录日志(如 DB 的 cache_invalid_log 表),离线Worker每分钟扫描未确认的删除并重试。
  5. 灰度上线:先对5%流量开启强一致方案,对比数据差异指标。
  6. 压测验证:使用并发写入脚本模拟竞态,验证最终数据是否线性一致。

3.7 验收标准

验收项校验方法通过条件
写后立即读取一致性并发写入程序,随后立刻读缓存每次读到的值等于最近一次成功写入的值(误差 < 5ms)
缓存空洞时间模拟单次写,记录缓存删除后到下次回填完成的时间P99 空洞时间 < 200ms
分布式锁死锁逃逸强制中断持有锁的线程后,等待锁超时自动释放锁在10s内自动释放,后续请求正常执行
补偿修复率人为注入缓存删除失败日志,观察补偿Job修复情况补偿覆盖率 ≥ 99.99%,修复延迟 < 1min
验收工具:建议使用 jepsen 或自定义 go-consistency-test 执行线性一致性检验,导出日志作为验收依据。