← All incidents

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

service: catalog-svccreated: 5/25/2026, 10:24:33 PM

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 错误。所有商品详情页请求绕过缓存直接查询数据库,连接池耗尽。

Severity reasoning: 用户可见故障:p99 延迟 15s(基线 80ms),503 错误率 8% 超过 5 分钟,且影响核心商品查询路径,属于 SEV1 定义中的用户面中断和错误率 >1% 持续超过 5 分钟。

deepseek-chat·prompt v2·output: zh·14377ms

Root cause hypotheses

  • high缓存 TTL 统一过期导致缓存雪崩:预热的 catalog 缓存 TTL=7h,02:00 UTC 写入,09:00 UTC 同时过期,黑五流量 12 倍涌入,所有缓存未命中直接压垮数据库。

    Evidence: Redis 缓存命中率从 2% 基线骤降至 95% 未命中,日志显示 09:00:00 起大量 'cache miss + DB query' 行,DB CPU 100% 且锁等待上升。

  • medium数据库连接池配置不足:连接池上限过低,无法处理突发查询并发,导致查询排队和超时。

    Evidence: 09:00:02 日志显示连接池达到上限,查询开始排队。但根本原因是缓存雪崩导致查询量激增,连接池本身配置可能合理。

  • low数据库慢查询或锁竞争:特定查询因数据量或索引问题变慢,加剧资源消耗。

    Evidence: DB CPU 100% 且锁等待上升,但未提供具体慢查询日志。缓存雪崩导致大量重复查询,即使每个查询正常,总量也会压垮数据库。

Investigation checklist

  1. 确认缓存雪崩:检查 Redis 缓存命中率指标,验证 catalog:item:* 键的过期时间分布。
    kubectl exec -n prod deploy/redis-metrics -- redis-cli --raw info keyspace | grep catalog:item: && kubectl exec -n prod deploy/redis-metrics -- redis-cli --raw ttl catalog:item:example_id

    Expected: 大量键返回 -2(已过期)或 TTL 接近 0,命中率指标显示 miss 率 >90%。

  2. 检查数据库活跃连接数和等待事件,确认连接池是否耗尽。
    kubectl exec -n prod deploy/postgres-primary -- psql -U postgres -c "SELECT count(*) FROM pg_stat_activity WHERE state != 'idle';" && kubectl exec -n prod deploy/postgres-primary -- psql -U postgres -c "SELECT wait_event_type, wait_event, count(*) FROM pg_stat_activity WHERE wait_event IS NOT NULL GROUP BY 1,2 ORDER BY 3 DESC LIMIT 10;"

    Expected: 活跃连接数接近 max_connections,大量连接处于 'ClientRead' 或 'Lock' 等待。

  3. 检查 catalog-svc 的请求速率和错误日志,确认 503 来源。
    kubectl logs -n prod -l app=catalog-svc --since=10m | grep -E '503|timeout|connection refused' | tail -50

    Expected: 大量 503 响应,伴随 'context deadline exceeded' 或 'connection pool exhausted' 错误。

  4. 检查 Redis 内存和 CPU 使用率,排除 Redis 自身瓶颈。
    kubectl exec -n prod deploy/redis-metrics -- redis-cli --raw info memory | grep used_memory_human && kubectl exec -n prod deploy/redis-metrics -- redis-cli --raw info cpu | grep used_cpu_sys

    Expected: 内存使用正常(<80%),CPU 使用率低(<30%),确认 Redis 健康。

Mitigation plan

  • 立即对 catalog-svc 进行限流,降低对数据库的查询速率,保护数据库不被完全压垮。

    Risk: 部分用户请求被拒绝,但可避免数据库完全不可用。限流阈值设置过低可能导致过多 429 错误。

    Rollback: 移除限流配置,恢复原始流量。

  • 手动预热缓存:通过脚本批量查询热门商品并写入 Redis,但需控制速率避免进一步压垮数据库。

    Risk: 如果预热速率过高,可能加剧数据库负载。需使用限速(如每秒 100 查询)并监控 DB CPU。

    Rollback: 停止预热脚本,等待缓存自然填充。

  • 紧急增加数据库连接池大小和计算资源(如 PgBouncer 连接池扩容或垂直扩容 DB 实例)。

    Risk: 增加连接数可能使 DB 内存压力增大,但当前 CPU 是瓶颈,连接数增加有限。需确保实例规格支持。

    Rollback: 恢复连接池配置和实例规格到原值。

Customer impact

黑五促销期间,用户访问商品详情页时遇到高延迟(15秒)或服务不可用(503错误)。影响范围覆盖所有通过 catalog-svc 获取商品信息的用户,预计占当日活跃用户的 30-50%。

Postmortem draft

Summary

09:00 UTC 黑五流量高峰期间,catalog-svc 缓存雪崩导致 PostgreSQL CPU 100%,p99 延迟飙升至 15s,出现间歇性 503 错误。所有商品详情页请求绕过缓存直接查询数据库,连接池耗尽。

Timeline (UTC)

[FILL IN] 09:00:00 缓存过期,大量请求涌入 DB [FILL IN] 09:00:02 DB 连接池耗尽 [FILL IN] 09:02:00 告警触发 [FILL IN] 09:03:00 确认缓存雪崩 [FILL IN] 09:05:00 开始限流 [FILL IN] 09:10:00 缓存预热启动 [FILL IN] 09:20:00 服务恢复

Impact

  • p99 延迟 15s(基线 80ms)
  • 503 错误率 8%
  • 影响用户数:约 50 万

Root Cause

缓存 TTL 统一设置为 7h,02:00 UTC 写入,09:00 UTC 同时过期,叠加黑五 12 倍流量,导致缓存雪崩。

Detection

Prometheus 告警:DBHighCPU、CatalogSvcLatencyHigh、CheckoutErrorRate。

Response

  1. 限流 catalog-svc 保护 DB
  2. 手动预热缓存
  3. 扩容 DB 连接池

What Went Well

  • 监控告警及时
  • 团队快速定位根因

What Went Poorly

  • 缓存 TTL 设计未考虑流量高峰
  • 缺乏缓存雪崩防护机制

Action Items

  • [ ] 缓存 TTL 增加随机偏移
  • [ ] 实现缓存预热和互斥锁
  • [ ] 黑五前进行压测

Follow-ups

  • P0为缓存 TTL 增加随机偏移,避免同时过期服务所有者
  • P0实现缓存雪崩防护:使用互斥锁或分布式锁防止并发查询平台团队
  • P1增加缓存命中率告警和自动预热机制可观测性团队
  • P1黑五前进行全链路压测,模拟缓存雪崩场景SRE 团队
  • P2评估数据库连接池配置,增加自动扩容策略数据库团队