跳转到指定楼层
上一主题 下一主题
收起左侧

[学习资料] 别再背延迟双删了...聊聊 MySQL+Redis 双写一致性在工业界的真实大坑

   
🔗
 楼主| 秀眉丽眼的木耳 2026-6-2 07:29:09 | 只看该作者
全局:
本帖最后由 秀眉丽眼的木耳 于 2026-6-1 16:30 编辑
syin2021 发表于 2026-6-1 06:00
时间戳比较的前提是读多写少,高写的场景时间戳失效导致的重试很快就会把数据库打爆了

这个问题切中要害了,高并发写的场景确实是分布式系统里最难搞的瓶颈。仔细盘一下的话,这里其实可以拆成三种不同的工业界业务场景来看,不同的场景解法完全不一样:

场景一:高频刚性写(每次写操作都必须立马落盘)
如果业务要求非常严格,每次写请求都必须实打实地更新数据库,那正如我前面在帖子里聊到的 Trade-off:**这本身就是一个典型的 CP 场景。在高频刚性写入下,强行在中间加一层 Redis 双写缓存,其实是增加系统的复杂度和抖动风险。**

这种高写场景的正确解法通常是直接去给数据库做**水平拆分(Sharding)、分库分表或者引入分布式存储**,靠底层存储的横向扩展能力去硬抗写入压力,此时 Redis 更适合只挂在下游当个只读副本,而不是频繁去做双写一致性控制。

场景二:用缓存去“聚合写操作”(写重缓冲)
如果这里的场景指的是:前端一秒钟过来 100 次写请求,我们先在 Redis 里用计数器或者内存数据结构缓存住,做高频更新;然后每隔 5 秒或者攒满 100 次,再由后台线程异步“批量一次性”刷进 MySQL(类似 Write-Back 模式)。

如果是这种“缓存挡写”的架构,那咱们今天讨论的时间戳/版本号双写校验机制**确实不适用,也根本不需要用**。因为两者的架构哲学完全相反:
* 咱们帖子里聊的方案,本质是 **Cache-Aside + 最终一致性**,数据库是绝对的真理源(Source of Truth),缓存只认比自己新的版本。
* 而这种聚合写方案,**Redis 变成了真理源**。写操作直接在缓存层就熔断、聚合了,流量根本不会在写的时候到达 DB,那就更谈不上“频繁失效重试打爆 DB”了。当然,这种方案的代价就是需要承担 Redis 宕机丢失一部分未刷盘数据的风险,工业界一般用在游戏玩家属性更新、或者非核心业务的计数器(比如视频播放量、帖子点赞数)上。

场景三:常规并发读写下,读请求碰上 Lua 脚本判定失败
如果回到最原始的 Cache-Aside 模型:写操作必须落盘,且并发极高,导致读请求拿到旧版本去回写 Redis 时被 Lua 脚本判定失败(返回 `0`)。

在真实的生产代码里,**读请求在 Lua 判定失败后,是绝对不需要、也不允许发起重试去查 DB 的**:
1. **Lua 失败意味着别人已经捷足先登了**。Lua 脚本之所以拒绝当前线程写入 Version 5,原因只有一个——Redis 里已经躺着一个更先进的 Version 6 了。既然缓存里已经有更新的数据了,说明缓存已经抢先一步达成了一致。读请求虽然在写缓存时“失败”了,但在业务上它直接大获全胜,顺手把 Redis 里的最新值捞出来返回给前端就完事了,没有任何重试回源 DB 的逻辑。
2. **退一万步说,在缓存短时间内频繁失效的极端瞬间,也有 SingleFlight 挡在最前面。** 无论并发进来多少个读请求,在单机内存里也只放行**唯一一个**去查 MySQL。写得再重,回源 DB 的压力也被死死限制在 `1`(按单机算)。下游 MySQL 感受到的只是轻风细雨,不存在被重试打爆的可能。

所以总结下来,工业界更讲究因地制宜:
* 要是高频刚性写,直接分库分表,不搞复杂的缓存双写;
* 要是想聚合写,走批量异步刷盘,Redis 当真理源;
* 要是常规高并发读写,走 SingleFlight + 读端不重试。

大家可以结合各自组里的实际业务场景,一起盘一下这个逻辑。

评分

参与人数 1大米 +1 收起 理由
jianhuiz + 1 赞一个

查看全部评分

回复

使用道具 举报

🔗
 楼主| 秀眉丽眼的木耳 2026-6-2 07:32:39 | 只看该作者
全局:
christina233 发表于 2026-6-1 09:19
真的没有意义,国人不知道但是只要态度谦虚我都给过,南亚某国人就算回答出花来一样给挂,一直问问到他不懂 ...

这个太对了。赞一个。
回复

使用道具 举报

🔗
 楼主| 秀眉丽眼的木耳 2026-6-2 07:33:49 | 只看该作者
全局:
地梩的匿名用户 发表于 2026-6-1 09:30
直接说没做过就挂得了。

要花多少年准备才能把所有这种程度的问题都准备的面面俱到。

其实并不多。前面有大佬也说过,很多时候也不需要这些,但是不影响我们持续学习。
回复

使用道具 举报

🔗
syin2021 2026-6-2 08:56:13 | 只看该作者
全局:
秀眉丽眼的木耳 发表于 2026-6-1 19:29
这个问题切中要害了,高并发写的场景确实是分布式系统里最难搞的瓶颈。仔细盘一下的话,这里其实可以拆成 ...

sr如果面试的时候不先问场景,流量直接开讲,直接就是red flag

写数据库这种强一致性的操作,提了分布式存储那能问的就更多了,回滚,冗灾都是坑
回复

使用道具 举报

🔗
regan2018nyc 2026-6-10 21:20:25 | 只看该作者
全局:
国内的那个味儿上来了 楼主
回复

使用道具 举报

🔗
fishingalone 2026-6-16 01:38:30 | 只看该作者
全局:
本帖最后由 fishingalone 于 2026-6-15 10:40 编辑

经验太少,没有看懂
A还没有删缓存,为什么B会cache miss?
A已经修改了DB,为什么B会读到旧的值?
回复

使用道具 举报

🔗
yF68U 2026-6-17 14:22:21 来自APP | 只看该作者
全局:
我说实话,工作里没遇到过这种问题,面试被问到了咋办
回复

使用道具 举报

🔗
徐晃班长 2026-6-17 21:36:52 | 只看该作者
全局:
本帖最后由 徐晃班长 于 2026-6-17 09:38 编辑

兄弟,我题目看了半天,还没看懂,可以解释一下吗?
“写线程 A 刚更新完 DB,还没来得及删缓存;读线程 B 刚好过来,缓存没中,去 DB 读了老数据。”

我假设是操作对象是一个record。简化来说, 一个key value pair - recordA

writerA 更新完DB中的dbRecordA之后,还没来得及删缓存cacheRecordA, readerB过来,
问题1)为什么缓存没中,writerA不是还没来得及删缓存吗?

readberB缓存没中,去 DB 读了老数据。
问题2)writerA不是刚更新完DB吗,为什么readerB从DB中读了老数据

希望楼主可以解答,谢谢!
回复

使用道具 举报

🔗
 楼主| 秀眉丽眼的木耳 2026-6-18 11:20:42 | 只看该作者
全局:
regan2018nyc 发表于 2026-6-10 06:20
国内的那个味儿上来了 楼主

如果你按照国内那个味解读,那就是国内那个味。如果你当作各种拉练,那就是一些讨论和学习的机会。
回复

使用道具 举报

🔗
 楼主| 秀眉丽眼的木耳 2026-6-18 11:41:42 | 只看该作者
全局:
徐晃班长 发表于 2026-6-17 06:36
兄弟,我题目看了半天,还没看懂,可以解释一下吗?
“写线程 A 刚更新完 DB,还没来得及删缓存;读线程 B ...

哈哈,握手。你卡壳的这两个地方,其实是很多人看八股文时最容易漏掉的隐形大坑。

其实道理特别简单,之所以觉得矛盾,是因为在高并发下,这几件事是在微秒级别同时发生的:

1. 为什么写线程还没删,读线程过来却没中?
因为这个缓存本来就是空的。它可能早就过期了,或者 Redis 内存满了被自动清理了。
时序是:缓存本来就没数据 -> 读线程过来一看,没中,准备去 DB 读。就在读线程刚迈出脚的这万分之一秒里,写线程突然杀出来把 DB 改了,但改完后写线程卡了一下,还没来得及去删缓存。这就碰上了。

2. 写线程刚改完 DB,为什么读线程还能读到老数据?
因为线上都是主从架构、读写分离的。
写线程改的是主库,读线程去的是从库。主库同步到从库有几个毫秒的延迟。读线程刚好在这一两毫秒内去从库一抓,精准读到了还没同步的老数据。

最绝的翻车现场,就是你提到的 5 和 6 完全反序的特殊情况:

【写线程】 ──> [2.更新DB主库] ─────────────────────────────> [6.终于缓过神删缓存(删了个寂寞)]
                                                                      ↑ (反序卡在这里)
【读线程】 ───────> [1.缓存没中] ──> [3.去从库读到老数据] ──────────────────> [5.慢吞吞把老数据写回Redis]

你看,写线程的第 6 步(删除)本来是想兜底的,结果因为卡顿,竟然落在了读线程第 5 步(回写)的后面。

写线程这一记大招直接劈空删了个寂寞,而读线程写进去的老数据成功在 Redis 里“买房”长住了,变成了永久的脏数据。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表