← All incidents

[Eval][v2][zh] Cache stampede after Redis key expiry on Black Friday morning

service: catalog-svccreated: 6/3/2026, 12:08:34 AM

Raw incident context

Time: 09:00 UTC, Black Friday. catalog-svc latency exploded at exactly 09:00:00 UTC.

Symptoms:
- catalog-svc p99: 15s (baseline 80ms)
- 503 rate: 8% (intermittent during DB overload)
- Postgres CPU: 100% sustained, lock waits climbing
- Redis CPU: 25% (looks healthy)
- Redis cache miss rate for keys matching "catalog:item:*": 95% (baseline 2%)
- All product detail page requests are flooding through to DB

Background:
- We pre-warm the homepage catalog cache every night at 02:00 UTC with TTL=7h
- 02:00 UTC + 7h = 09:00 UTC ← all keys expired simultaneously
- Black Friday traffic ramp: 12x normal at 09:00 UTC (marketing email blast)
- No per-key locking; every cache miss triggers a fresh DB query

Logs:
- 08:59:58 UTC — last cache hit logged
- 09:00:00 UTC — first wave of "cache miss + DB query" log lines
- 09:00:02 UTC — DB connection pool hits ceiling, queries start queueing

On-call:
- 09:02 — pager (multiple alerts: DBHighCPU, CatalogSvcLatencyHigh, CheckoutErrorRate)
- 09:03 — diagnosis: cache stampede confirmed by Redis miss-rate metric
- 09:05 — debating: extend TTL (won't help in flight), warm cache manually (DB is the bottleneck), or shed load

Summary

SEV1

09:00 UTC 黑五流量高峰期间,catalog-svc 缓存雪崩导致 PostgreSQL CPU 100%、p99 延迟 15s、间歇性 503 错误。缓存 TTL 统一在 09:00 过期,所有请求穿透至数据库,引发连锁故障。

Severity reasoning: 用户面故障:p99 延迟 15s(基线 80ms),503 错误率 8% 超过 1% 阈值且持续超过 5 分钟,收入路径(商品详情页)中断,符合 SEV1 定义。

deepseek-chat·prompt v2·output: zh·15125ms·2216↑ / 2039↓ tok·$0.00284

Root cause hypotheses

  • high缓存 TTL 统一设置为 7 小时,导致所有缓存键在 09:00 同时过期,引发缓存雪崩

    Evidence: Redis 缓存命中率从 2% 骤降至 95% 未命中,且最后缓存命中日志在 08:59:58,与 TTL 过期时间吻合

  • high黑五流量 12 倍于正常,数据库连接池耗尽导致查询排队和超时

    Evidence: 09:00:02 数据库连接池达到上限,查询开始排队,DB CPU 100% 持续

  • medium数据库锁竞争加剧,慢查询阻塞其他查询

    Evidence: Postgres 锁等待指标上升,但未提供具体慢查询日志,需进一步验证

  • medium应用层未实现缓存击穿保护(如互斥锁或提前刷新)

    Evidence: 每个缓存未命中都直接触发数据库查询,无任何保护机制

  • lowRedis 实例性能瓶颈导致缓存读取延迟

    Evidence: Redis CPU 仅 25%,但缓存未命中率高,排除此假设

Investigation checklist

  1. 确认缓存雪崩范围:检查 Redis 中 catalog:item:* 键的 TTL 分布
    redis-cli -h <redis-host> -p 6379 SCAN 0 MATCH 'catalog:item:*' COUNT 1000 | xargs -I {} redis-cli -h <redis-host> -p 6379 TTL {} | sort | uniq -c

    Expected: 大量键 TTL 为 0 或接近 0,确认同时过期

  2. 检查数据库当前活跃查询和等待事件
    SELECT pid, query_start, state, wait_event_type, wait_event, query FROM pg_stat_activity WHERE state != 'idle' ORDER BY query_start LIMIT 20;

    Expected: 大量查询处于 'active' 状态,wait_event 可能为 'LWLock' 或 'ClientRead'

  3. 检查数据库连接池使用率
    SELECT count(*) AS used_connections, setting AS max_connections FROM pg_stat_activity, pg_settings WHERE name = 'max_connections' GROUP BY setting;

    Expected: used_connections 接近 max_connections,确认连接池耗尽

  4. 检查应用日志中缓存未命中的频率
    kubectl logs -n prod -l app=catalog-svc --since=15m | grep -c 'cache miss'

    Expected: 日志行数远高于正常值(正常每分钟 < 100,当前可能 > 10000)

  5. 检查 Redis 内存使用和逐出策略
    redis-cli -h <redis-host> -p 6379 INFO memory | grep -E 'used_memory_human|maxmemory_human|evicted_keys'

    Expected: 内存使用未达上限,逐出键数为 0,排除内存压力

Mitigation plan

  • 立即启用缓存击穿保护:在应用层对缓存未命中加互斥锁,只允许一个请求回源数据库,其余等待缓存填充

    Risk: 锁等待可能导致请求超时,但比直接压垮数据库安全;需确保锁超时机制

    Rollback: 回滚代码变更或通过配置开关禁用互斥锁

  • 手动预热缓存:从数据库导出热门商品数据,批量写入 Redis,设置随机 TTL(如 6-8 小时)

    Risk: 预热脚本可能进一步增加数据库负载,需在低峰期执行或限制并发

    Rollback: 停止预热脚本,清除已写入的缓存键

  • 对 catalog-svc 实施限流:在入口处拒绝部分请求,保护数据库不被完全压垮

    Risk: 用户会看到更多 503 错误,但可防止系统完全崩溃

    Rollback: 移除限流规则或提高限流阈值

  • 扩展数据库读副本:临时增加只读副本分担查询负载

    Risk: 副本同步延迟可能导致数据不一致,且扩容需要时间

    Rollback: 缩容副本

Customer impact

黑五期间,用户访问商品详情页时遇到高延迟(15 秒)或间歇性 503 错误,影响所有通过 catalog-svc 获取商品信息的用户。预计受影响用户数占当日活跃用户的 80% 以上。

Postmortem draft

Summary

09:00 UTC 黑五流量高峰期间,catalog-svc 缓存雪崩导致 PostgreSQL CPU 100%、p99 延迟 15s、间歇性 503 错误。缓存 TTL 统一在 09:00 过期,所有请求穿透至数据库,引发连锁故障。

Timeline (UTC)

  • 02:00 — 预缓存预热完成,TTL 设置为 7h
  • 08:59:58 — 最后一个缓存命中
  • 09:00:00 — 缓存雪崩开始,大量缓存未命中日志
  • 09:00:02 — 数据库连接池耗尽,查询排队
  • 09:02 — 告警触发(DBHighCPU, CatalogSvcLatencyHigh, CheckoutErrorRate)
  • 09:03 — 诊断确认缓存雪崩
  • [FILL IN] — 缓解措施实施时间
  • [FILL IN] — 服务恢复时间

Impact

  • p99 延迟从 80ms 升至 15s
  • 503 错误率 8%
  • 影响所有商品详情页请求

Root Cause

缓存 TTL 统一设置为 7 小时,所有键在 09:00 同时过期,叠加黑福 12 倍流量,导致数据库过载。

Detection

告警系统在 09:02 触发,但缓存雪崩在 09:00 已开始,检测延迟 2 分钟。

Response

  • 09:03 确认根因
  • [FILL IN] 实施缓解措施

What Went Well

  • 告警配置覆盖关键指标
  • 缓存命中率监控帮助快速定位

What Went Poorly

  • 缓存 TTL 未添加随机偏移
  • 无缓存击穿保护
  • 黑五流量预估不足

Action Items

  • [ ] 缓存 TTL 添加随机偏移(±30%),避免同时过期
  • [ ] 实现缓存击穿保护(互斥锁或提前刷新)
  • [ ] 黑五前进行负载测试,验证缓存策略
  • [ ] 增加数据库连接池监控告警

Follow-ups

  • P0为缓存 TTL 添加随机偏移,避免同时过期catalog-svc 服务负责人
  • P0实现缓存击穿保护机制(互斥锁或提前刷新)catalog-svc 服务负责人
  • P0黑五前进行全链路负载测试,验证缓存策略和数据库容量SRE 团队
  • P1增加数据库连接池使用率和锁等待的告警平台团队
  • P1评估数据库只读副本的扩容自动化平台团队
  • P1制定黑五流量预案,包括限流和降级策略SRE 团队