07.04 缓存过载与热点:群聊预览失效时谁承压
返回第七卷 · 状态前置:07.01 · Redis 前置:07.02 · 缓存交错前置:07.03 · 测量前置:11.02
07.04 缓存过载与热点:群聊预览失效时谁承压
DeepTutor 八节初稿经技术与教学审阅后的静态课程。1000 请求、每秒 200 次回源能力、4 个网关、100 个键和 10000 名成员全是纸上假设,不是运行或容量结论;没有运行 Redis、数据库、Go、负载、故障或站点,也不声称 OpenIM 实际如此部署。预览是未来 S3 教学数据库权威消息的派生视图;当前 S2
accepted_in_memory的承诺不变。
一、一个“缓存未命中”可能把谁压倒?
07.03 已画出单个 preview:c-a 的读写竞态。现在换成虚构大群 c-g,许多成员反复打开会话列表,派生键 preview:c-g 可能很热。假设某一观察窗口里1000 个请求在同一个过期点、首个回填完成前同时读这个键,都看到 miss;若每个请求独立查权威数据库,便会有 1000 次回源尝试。这叫“热门缓存项失效引起的请求群”或常称缓存击穿,瓶颈可能从 Redis 转移到数据库。Redis cache-aside 文档
为算资源账,另假设权威数据库对这类预览查询可安全完成 200 次/秒,且忽略其他负载、网络和动态扩容。这里 1000/200 是 5 倍瞬时需求。若所有 1000 次都排队且服务率恰好 200 次/秒,第一个整秒最多完成 200,剩 至少 800 未完成,理想地至少要约 5 秒处理完这一批;若系统有有界队列,超额会被拒绝或降级而非全部排队。若不是一次突发,而是持续每秒 1000 次都 miss,且不拒绝,积压每秒净增 1000−200=800,不会自行排空。不能把“一次突发的剩余 800”和“每秒增加 800”混为同一指标。
| 第一遍 | 第二遍 | 交付物 |
|---|---|---|
| 热键、击穿/穿透/雪崩、大键的不同原因 | 合并加载、错开过期、有界回源、降级与证据 | 四组纸上算式、一张“症状→措施→限制”矩阵 |
二、击穿:一个热键过期,1000 个读者同时去查库
热键是访问高度集中到一个键;击穿是这类键刚好不可用时,许多请求一起穿过缓存查下游。两者相关但不等同:preview:c-g 在缓存仍 hit 时可能让 Redis 自己承受每秒 1000 次访问,却未必压数据库;一旦它过期,才可能产生上节的 1000 次回源。在本题的“一批同时到达且首个填入前全部 miss”前提下,后续某请求填回缓存不能倒转已经发出去的 1000 个数据库请求。Redis cache-aside 文档
t0: preview:c-g TTL 到期
t1: 请求 R1..R1000 都读到 miss(尚无任何回填)
t2: 无合并时,各自尝试查权威;DB 安全能力 200/s
t3: 一部分完成,余下排队、超时或被明确拒绝这里的“200/s”是安全预算,不是让服务先收下全部请求再祈祷数据库撑住;连接池等待、超时和重试可能使下游更忙。还要区分每秒请求数与某次请求 P95:一个是速率,一个是时间分布,不能由 1000/200=5 推出“P95 一定涨五倍”。排障应记录该键命中/失效时刻、同窗回源量、DB 队列/连接等待、错误和用户可见结果,而非只看平均命中率。Redis 监控指导
三、合并加载:四个网关各一份锁,仍可能四次回源
同键合并加载(singleflight)的想法是让同时 miss 的同一进程请求等待一个“领头加载者”,拿到结果后共享。假设 1000 请求平均落到4 个独立网关进程,每个进程各收到 250 个;如果每个进程都能把本进程对 preview:c-g 的 250 次 miss 合为一次,且领头加载成功、等待者不提前超时,那么这次理想状态是 4 次数据库查询,不是全系统只查 1 次。进程内机制没有跨进程协调能力。
领头请求若超时、取消或进程崩溃,等待者不能无限挂起;要有等待期限、错误传播、重新选领头者或降级办法。若一台网关同时为几千个不同键启动领头加载,仍可能耗尽连接池;因此还需全局或每目标有界并发。用 Redis 分布式锁可能减少跨进程重复加载,但锁过期、持有者停顿、迟到填入与 07.03 的 V8/V9 竞态仍要单独处理,不能把“加锁”当完整正确性证明。Redis 官方 cache-aside 文档也把同键加载与缓存过载视为要评估的机制。Redis cache-aside 文档
| 情况 | 理想回源量 | 仍需处理 |
|---|---|---|
| 无合并,1000 次均已 miss | 1000 | DB 超预算、排队/拒绝 |
| 四进程各自同键合并成功 | 4 | 领头失败、跨进程仍重复、读到旧版本 |
| 所谓“全局锁”但无期限/围栏 | 无可靠数字 | 旧持有者、丢锁、回填竞态、超时 |
四、雪崩:很多键一起到期,和单热键不同
另设 100 个不同预览键都在 t=60 秒过期,每键在首个回填完成前各有 10 个同时到达的读者:100×10=1000 次 miss/回源尝试。与击穿一个 c-g 热键相比,这次压力来自许多键同步失效,常称缓存雪崩。缓存节点故障导致大量键一起不可用也可能产生类似汇总回源,但触发原因不是 TTL 到时。Redis cache-aside 文档
若把这 100 个键的到期理想地均匀分布到 10 秒,每秒约 100/10=10 个键到期;按本题每键 10 次同时 miss,量级可降到约 100 次回源/秒,低于纸上 200/s 预算。这是一个理想均匀算式,不是“随机加 TTL 抖动必然均匀”的保证:真实流量、热点、时钟、缓存故障和回填时长会让同秒落点集中。错开许多键的过期也不能单独解决一个超热键瞬时 1000 请求同时 miss,仍需合并加载与容量边界。
预热、分批失效、TTL 错开、有界回源和可降级展示可组合,但要说清各自服务的故障模型。若缓存被整体清空,TTL 错开已失去意义;若 DB 不能处理峰值,必须保护权威源,而非让所有请求无界排队。Redis Key Eviction
五、穿透:每次问一个不存在的新 ID,负缓存也追不上首查
缓存穿透看的是大量请求直接落到“缓存没有、权威也没有”的路径。假设攻击或错误客户端连续请求 m-x1...m-x1000 这 1000 个各不相同的不存在 ID,每个只查一次。即便对每个 ID 的权威真无结果写负缓存,这第一批仍需 1000 次权威查验,因为没有重复键可命中;之后若重复请求同一个 m-x1,受控负缓存才可能减少回源。按 ID 无界创建负键本身还会消耗内存。Redis cache-aside 文档
07.03 的两条边界在此更重要:数据库错误/超时不是“真无”,不能写负缓存;u-c 查私有 c-a/m-9 得隐藏 404 是授权结果,不能按消息 ID 向所有用户缓存“不存在”。可以评估有界输入、可信主体限流、请求预算、负缓存或过滤器,但过滤器需要完整来源与更新规则,可能误判“可能存在”;若它因同步延迟把新插入的 m-9 当“不存在”,又会制造错误拒绝。本章不把任何一个预过滤结构当默认安全答案。
| 请求分布 | 负缓存第一批的作用 | 风险 |
|---|---|---|
1000 次同一个真不存在 m-x | 首查回源,后续可命中 | 插入竞态、权限范围、TTL |
| 1000 次各一个随机真不存在 ID | 首查仍可能 1000 次回源 | 负键爆量,不能靠命中挡住第一批 |
u-c 对私有 m-9 得隐藏 404 | 不得据此写全局真无 | 污染有权用户的读取 |
六、热键与大键:即使命中,也可能压 Redis
preview:c-g 每秒 1000 次读取,哪怕 100% hit,请求仍集中到 Redis 上相同的键和其网络/计算路径;这叫热键,可以在不打数据库的情况下压高缓存层延迟。分散只读请求、应用本地短缓存或复制读路径可能有帮助,但会引入新一层失效/陈旧政策,不能只报命中率。Redis 官方将 hot keys 和 large keys 列为不同访问反模式。Redis 监控指导
另一例:候选成员 Set candidate_members:c-g 含 10000 个用户 ID。一次 SISMEMBER 只问一个候选是否在 Set,和每次用 SMEMBERS 把 10000 个 ID 全取回不是同样的传输与内存工作。这个 Set 可能成为大键;大键即使访问频率不高,一次全量读取/迁移/删除也可能昂贵。它仍只是候选成员缓存,不能独立裁决当前资格。热键描述访问集中,大键描述值/元素规模,可同时存在也可分别存在。Redis Sets · Redis 监控指导
不要凭“10000 成员”推算实际字节或 P95;用户 ID 长度、Redis 编码、返回协议、网络和操作选择都未给。可以用有界查询、按业务范围拆分或维护派生索引,但需要说明重建与权限版本;“把 Set 分片”也不自动降低一个非常热的成员查询或解决授权失效。
七、缓存坏了:先保权威数据库,再谈旧预览降级
设 Redis 此刻不可用,原本可命中的请求都要走其他路径;若每秒 1000 次都直接回源,而数据库对这类查询安全预算仅 200/s,持续净压力超出 800/s。可用有界并发/队列、按身份/会话限流、明确过载响应保护数据库。能否返回短时旧预览,要看产品是否允许及是否还有受控、未越权的本地副本;权限旧许可不能为继续发送开后门,在线缺键也应按未知/降级政策而非假装“设备一定离线”。DB 已受压时无界重试只会制造更多工作。
观测矩阵需把缓存服务与用户结果放在同一时间窗:cache hits/misses、expired/evicted、Redis 命令/网络/内存、按键热度的受控采样或 Top-K、单键大小/元素数、DB 回源 QPS/连接等待/错误、请求 P95/P99、返回旧值比例和被限流/拒绝量。不要把每个 conversation_id 或 user_id 直接当指标标签制造无界基数,也不能仅凭 Redis 自身延迟正常就断言整个历史接口没变慢。Redis 观测指导 · 11.03 指标口径
| 症状 | 首选问题 | 候选措施及限制 |
|---|---|---|
| 单热键过期、同刻大量回源 | 击穿? | 同键合并加载、有界回源;进程内只合本进程 |
| 许多键同刻 miss | 雪崩? | 错开到期/预热;不能修单热键或整节点故障 |
| 随机不存在 ID 持续回源 | 穿透? | 限流/输入约束/受控负缓存;首查仍要证据 |
| 高 hit 但 Redis 延迟升 | 热键或大键? | 热度/操作大小测量;本地缓存会加一致性负担 |
| Redis 故障、DB 回源超预算 | 保护权威? | 限制请求/明确降级,权限不得凭旧许可放行 |
八、交付能区分四种问题的容量账
交付:1000 个同键 miss 对 200/s 的队列/拒绝算式、四网关理想同键合并后 4 次加载、100 键错开 10 秒的理想 100/s、1000 个随机不存在 ID 的首查压力、c-g 热键与 10000 成员大键的对照。再给每个措施写清它解决哪一条路径、仍缺什么安全/一致性证据、失败时怎样保 DB。这些数字只教算量级,不是实际系统容量评估。
分层练习与反馈
1–8 认症状,9–16 手算工作量,17–22 评审降级和证据。先预测,再展开答案。
1. 热键与击穿是同一个词吗?
不是。热键是访问高度集中;击穿是热门键不可用时请求同时回源。
2. 高命中率能排除热键压 Redis 吗?
不能。即使 100% hit,同一键仍可能承受高请求量。
3. 一个键过期和 100 个键同刻过期分别更像什么?
前者若是热键可造成击穿;后者汇总失效更像雪崩。
4. 1000 个随机不存在 ID、每个只查一次,主要是什么风险?
穿透:首批都可能回源,逐 ID 负缓存没有可复用的第二次命中。
5. 10000 成员 Set 每次全取,与每次只测一个成员一样吗?
不一样。SMEMBERS 全取结果量大,SISMEMBER 只问一个候选是否在集合。
6. 大键一定也是热键吗?
不一定。前者说值/元素规模,后者说请求集中程度;可独立出现。
7. `u-c` 私有隐藏 404 能按 `m-9` 全局负缓存吗?
不能。它是主体相关授权结果,不证明 m-9 真不存在。
8. 缓存故障就能凭旧成员缓存放行发送吗?
不能。权限要有足够新的权威证据,不能用旧许可作降级。
9. 1000 次同键请求均在首个回填前 miss,无合并时有多少回源尝试?
1000 次,前提是每个 miss 都独立回源。
10. DB 安全处理 200/s,首个整秒最多处理本批几次?还有几次未完成?
最多 200;若未拒绝,至少 800 仍等待。若有有界拒绝,应分别统计拒绝量。
11. 只有这一批 1000 次,理想 200/s 至少约几秒清完?
约 5 秒,忽略其他负载、排队开销和重试;不是实测 P95。
12. 若持续每秒 1000 次全 miss 且从不拒绝,积压每秒净增多少?
1000−200=800 次/秒,不能靠等待自己消失。
13. 四网关各 250,同键进程内合并理想多少次回源?
4 次,每进程 1 次;不是全系统 1 次。
14. 合并加载的领头请求失败,其他 249 请求能无限等吗?
不能。需有等待期限、错误传播/重选和 DB 并发预算。
15. 100 个键每键 10 次同时 miss,共多少次回源?
无合并且首个回填前都 miss 时是 100×10=1000 次。
16. 理想均匀错开 10 秒,约每秒多少次 miss?
约 10 键/秒×10 次/键=100 次/秒;真实到期和流量未必均匀。
17. TTL 抖动能单独解决 `preview:c-g` 一个键的 1000 次同刻 miss 吗?
不能。它主要分散多键同时失效;单键仍需合并加载/限流。
18. 1000 随机真不存在 ID 都写负缓存,第一轮 DB 压力会变零吗?
不会。每个 ID 首查仍要权威判断,还会创建很多负键。
19. Redis 失效后 1000/s 全回源,而 DB 预算 200/s,应先做什么?
限制并发/队列、限流或明确拒绝/降级,保护权威源;不能无界重试。
20. 允许短时旧预览,就可以允许旧权限放行吗?
不能。预览派生值可否陈旧和成员授权安全是不同合同。
21. 只看平均缓存命中率足以发现这次雪崩吗?
不足。要看同窗峰值 miss、DB 回源/等待/错误和用户 P95/P99。
22. 热键观测为何不直接把所有会话 ID 作指标标签?
会产生无界高基数指标;应使用受控采样、Top-K 和脱敏关联日志。
本章完成标准与下一步
不看答案时,能把单热键过期、百键同过期、千个随机不存在 ID、万人成员大键分别分类并手算 1000/200、四网关合并、错开过期三组账;还能说明缓存故障时哪种旧状态可按合同降级、哪种权限绝不可旧读放行,才算完成第一轮。第二轮由学习者在隔离环境收集真实请求分布和限流/故障结果;本文没有代替负载测试。
按学习路线,下一章07.05 缓存服务的持久与故障将检查缓存服务自身的快照、日志、复制与故障边界;07.06 再进入队列、发布订阅和可回放日志。