知道,和做到之间
这一系列讲的所有机制,出事的时候团队几乎都「知道」——方案评审时提过、code review 时标过、事故复盘时写回过。知道与做到之间隔着的,就是这七个故事。案例形态取自真实事故的常见样貌,数据做了脱敏处理。
事故一:统一 TTL 的集体失效
现象:凌晨两点 DB 告警炸群,详情接口 RT 从 20ms 飙到 2s。根因:三天前的批量预热脚本给五十万热点数据统一设了「24 小时」TTL,同一秒集体过期,五万 QPS 全量回源——雪崩篇的标准剧本,一字不差。修复:TTL 加随机抖动重新预热;教训:预热脚本上线要过评审,「统一 TTL」这种写法直接打回。
事故二:先删缓存留下的旧值
现象:商品改价后,部分用户页面显示旧价,持续了一整晚。根因:写接口的顺序是先删缓存再更新库——删完到库更新完成之间,回源请求把旧值读出来又填了回去,旧值驻留到次日 TTL 到期(一致性篇的危险时序,真实发生)。修复:改为先更新库再删缓存,补上延迟双删;教训:读写交错不是理论概率,DB 慢一秒它就发生一次。
事故三:扩容解决不了的热 Key
现象:大促期间某分片 CPU 100%,运营要求「再加一台 Redis」。根因:一个爆款商品的 key 扛了三十万 QPS,key 哈希到固定槽住在固定分片——加机器它也不会搬家(热 Key 篇开头的第一句话)。修复:该 key 改多副本打散 + 应用侧本地缓存;教训:扩容前先看 key 级别的热度分布,别对单 key 风暴开错药。
事故四:DEL 卡住整个实例
现象:Redis 命令延迟周期性尖刺,每次两秒左右,找不到慢查询。根因:定时清理任务在删一个八百万成员的 set,DEL 逐个释放内存阻塞单线程——慢查询日志里看不到它,因为被卡的全体请求都在排队(大 Key 篇的连锁反应第一条)。修复:DEL 全部换 UNLINK,大 set 用 SCAN 分批清理;教训:单线程模型的 Redis,任何大动作都要问一句「这会占主线程多久」。
事故五:断线引发的全量同步风暴
现象:机房网络抖动两分钟,恢复后主库 CPU 打满、全站缓存接口变慢。根因:repl_backlog 用默认 1MB,写流量大的主库两分钟就把环形缓冲区覆盖了,从库 offset 失效,全部退化成全量同步——bgsave 加 RDB 传输的风暴(过期与复制篇的警告成真)。修复:repl-backlog-size 调到 256MB;教训:backlog 大小要用「写 QPS × 最长可容忍断线时间」算,不是默认值。
事故六:上线才炸的跨槽 MGET
现象:迁到 Cluster 后上线十分钟,批量接口大面积报错。根因:单机时代随手用的 MGET,在 Cluster 上 key 分散在不同槽,直接报 CROSSSLOT——开发环境三个 key 恰好同槽,测试全绿(Cluster 篇的代价清单第一条)。修复:客户端按槽分组再 pipeline;教训:跨槽限制要写进团队规范,测试环境的数据分布骗得过所有人。
事故七:脑裂蒸发的那几秒
现象:网络分区恢复后,用户投诉订单状态「回退」了。根因:分区期间哨兵完成切主,旧主还在孤立地接受写入;恢复后旧主降级从库并全量同步新主,分区期间写进旧主的数据全部蒸发(哨兵篇的脑裂暗坑)。修复:配置 min-replicas-to-write 1,孤立主库拒绝写入;教训:状态类数据放缓存要有「可能被蒸发」的心理预期,关键状态落库。
七个事故,七个教训,全部能在前面十六篇里找到机制原型——事故不是新知识,是没被执行的旧知识。最后一篇收官:把整个系列收进一张进阶地图。
评论 (0)