2026 年了,很多系统依然在为“缓存和数据库不一致”头疼。

有人说:“缓存过期就行啦,脏数据最多存在几分钟,用户又不会死。” 有人却说:“我们金融场景,脏数据哪怕存在 500ms 都是事故。”

真相是:没有一种方案是绝对完美的,只有适合你业务容忍度的方案

今天我们把目前主流的几种做法全部摆上台面,逐一拆解优缺点,并配上真实的业务案例,帮助你在下一次架构评审或双十一压测前,做出更清醒的选择。

一、先说结论:目前最推荐的组合(2025-2026 大厂主流)

读写比例 一致性要求 推荐方案组合 脏数据最坏窗口 复杂度
读 >> 写 允许几秒~几分钟 Cache-Aside + 先更新库后删缓存 + 延时双删 缓存剩余 TTL ★★☆☆☆
读 >> 写 希望 < 1~3秒 Cache-Aside + 先更新库后删缓存 + MQ异步删 通常 < 1s ★★★★☆
读 >> 写 极致最终一致 Cache-Aside + Canal + MQ 毫秒~秒级 ★★★★★
写 ≈ 读 允许丢失少量 先写 Redis → 异步批量回写 MySQL 可丢失几秒数据 ★★★★☆
写 > 读 必须强一致 只用 MySQL + 分布式锁(基本放弃 Redis) 无脏数据 ★★★★★★

绝大多数 ToC 业务(商品、内容、推荐、用户资料等)最终会落在第 2 或第 3 行。

下面我们逐个拆解,并通过案例说明这些组合的实际运用。

二、方案详解 + 真实案例

1.方案 1 – Cache Aside(旁路缓存) + “先更新库,后删缓存”

(1)基本流程
写流程:
1. 更新 MySQL
2. 删除 Redis(不管成功与否)

读流程:
1. 先查 Redis,命中 → 返回
2. 未命中 → 查 MySQL → 回写 Redis → 返回

(2)最坏情况举例(经典脏数据案例)

场景:商品详情页,缓存 TTL = 10 分钟

时间线:

  • T0:管理员把价格从 99 → 199(更新 MySQL 成功)
  • T0+100ms:删缓存失败(网络抖动、Redis 慢)
  • T0+200ms:用户 A 访问详情页 → Redis 还是旧值 99 → 回写 Redis(更糟糕,把 99 又写回去了)
  • T0+10min:缓存过期,用户下次访问才看到 199

脏数据持续时间:最坏 10 分钟

(3)改进版 – 延时双删!!!

更新 MySQL → 删除 Redis → sleep(500~1000ms) → 再次删除 Redis

注意:

为什么 sleep 有效?
因为在“先更新库 → 删缓存”之后,如果有读请求在“更新完成但第一次删还没成功”这个极短窗口读到了旧值并回写了 Redis,那么第二次删就能把这个脏值删掉。

真实案例:
某中型电商 2023~2025 年一直在用延时双删 + 1秒 sleep,脏数据投诉率从每月几十条降到每月 1~2 条,可接受。

2.方案 2 – MQ 异步删缓存(目前性价比最高的生产方案)

(1)流程

1. 更新 MySQL(事务内)
2. 事务提交成功后 → 发 MQ 消息(payload 包含 key 或 key 前缀)
3. MQ 消费者收到消息 → 删除对应 Redis key
   └─ 失败 → 重试(指数退避)或进死信队列

(2)优点
  • 删缓存失败不影响主流程
  • MQ 持久化 + 多消费者 → 高可用
  • 可批量删(前缀删除)
(3)真实案例:某内容平台(日活千万级)
  • 文章审核通过后修改标题、正文
  • 发 MQ 消息 “article:update:{id}”
  • 消费者收到后删 article:{id} 和 article:feed:tag:{tagId} 等相关 key
  • 即使审核服务重启、网络分区,MQ 重试机制保证最终删除
  • 脏数据窗口通常在 200ms~2s 内(取决于 MQ 积压情况)

3.方案 3 – Binlog 监听(Canal / Debezium) + MQ

MySQL → binlog → Canal/Debezium → 解析变更 → 发 MQ(或直接推 Redis)
→ 消费者根据变更类型删/更新对应缓存

(1)优点
  • 完全解耦,业务代码几乎零侵入
  • 即使服务重启、Redis 短暂不可用,最终也能对齐
  • 支持复杂场景(比如一张表变更影响多个 key)
(2)真实案例:某直播平台礼物背包、用户资产
  • 用户送礼 → 扣减背包、加主播收入
  • Canal 监听到 users、gift_bag、broadcaster_income 三张表变更
  • 统一推到 MQ topic “cache-invalidate”
  • 消费者根据表名 + 主键 → 删除对应 Redis key(支持前缀、Hash 字段级删除)

脏数据窗口通常在 100ms~800ms,非常稳定。

4.方案 4 – 先写 Redis,后异步写 MySQL(计数器/点赞/浏览量专用)

(1)流程

点赞功能:
1. Redis INCR like_count:{postId}
2. 返回成功给用户
3. 异步任务(每 3~10 秒)批量 flush 到 MySQL

(2)真实案例:短视频平台日增赞 10 亿+
  • 几乎所有点赞操作只走 Redis
  • 每 5 秒一个定时任务 select + update + delete 批量同步
  • 极端宕机丢失 5 秒数据 → 运营可接受(会补赞活动)
  • 读性能极致,写性能极致

三、最后一次总结表格(建议收藏)

你最在意的点 推荐方案 预计脏数据时长 引入组件 团队维护成本
简单、成本最低 延时双删 几秒~TTL 极低
中等并发,要稳定 先更新库 + MQ 异步删 < 3s MQ
高并发、大流量 Canal + MQ < 1s Canal + MQ
纯计数器、允许少量丢失 先写 Redis → 异步回写 MySQL 可丢失几秒 定时任务
金融、对账、绝对不能脏 直接走 MySQL + 分布式锁(慎用 Redis) 分布式锁

选方案的时候,先问自己三个问题:

  1. 脏数据最多能忍几秒?(用户能感知吗?)
  2. 写 QPS 峰值大概多少?(会不会把 MQ 打爆?)
  3. 团队愿意为一致性多维护几套中间件吗?

欢迎留言区讨论:
你们现在用的是哪种方案?
遇到的最严重的一次不一致事故是什么样的?

希望这篇对你有帮助~记得点赞收藏。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐