← All incidents

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

service: catalog-svccreated: 6/2/2026, 11:41:48 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 因缓存雪崩导致数据库 CPU 100%,p99 延迟飙升至 15 秒,间歇性 503 错误。所有商品详情页请求绕过缓存直接查询数据库,影响用户浏览和下单。

Severity reasoning: 用户可见的故障(503 错误率 8% > 1% 且持续超过 5 分钟),p99 延迟 15 秒远超基线 80ms,收入路径(商品详情页)中断,符合 SEV1 标准。

deepseek-chat·prompt v2·output: zh·12396ms·2216↑ / 1875↓ tok·$0.00266

Root cause hypotheses

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

    Evidence: Redis 缓存命中率从基线 2% 飙升至 95% 的 miss 率,且最后缓存命中日志在 08:59:58,09:00:00 开始出现大量缓存未命中日志。

  • high黑五流量 12 倍于正常,数据库连接池耗尽,查询排队导致 CPU 100%。

    Evidence: 09:00:02 日志显示连接池达到上限,查询开始排队;Postgres CPU 100% 持续。

  • medium数据库查询未优化,全表扫描或缺少索引导致单个查询耗时过长。

    Evidence: p99 延迟 15 秒,但基线仅 80ms,可能因大量并发查询导致锁竞争或执行计划变差。

  • lowRedis 实例配置问题(如 maxmemory-policy 导致键驱逐)加剧缓存 miss。

    Evidence: Redis CPU 仅 25%,但 miss 率异常高,需检查驱逐策略和内存使用。

  • low应用层缓存穿透,恶意请求或爬虫绕过缓存直接请求数据库。

    Evidence: 所有请求均命中缓存 miss 模式,但无特定 IP 异常流量证据。

Investigation checklist

  1. 确认缓存雪崩:检查 Redis 中 catalog:item:* 键的 TTL 分布
    redis-cli -h <redis-host> -p 6379 --scan --pattern 'catalog:item:*' | head -100 | 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, query FROM pg_stat_activity WHERE state != 'idle' ORDER BY query_start LIMIT 20;

    Expected: 大量查询处于 'active' 状态,wait_event_type 可能为 'Lock',查询内容为商品详情 SQL

  3. 检查数据库连接池使用率
    SELECT count(*) FROM pg_stat_activity;

    Expected: 连接数接近 max_connections 配置值

  4. 检查 Redis 内存使用和驱逐策略
    redis-cli -h <redis-host> -p 6379 INFO memory | grep -E 'used_memory|maxmemory|evicted_keys'

    Expected: used_memory 未接近 maxmemory,evicted_keys 为 0,排除驱逐导致 miss

  5. 检查应用日志中是否有异常请求模式
    kubectl logs -n prod -l app=catalog-svc --since=30m | grep -iE 'cache miss|slow query' | head -50

    Expected: 大量 'cache miss' 日志,无特定 IP 重复请求

Mitigation plan

  • 立即启用缓存预热:手动将热门商品数据写入 Redis,设置随机 TTL 分散过期时间

    Risk: 预热过程会额外增加数据库负载,可能加剧当前问题。需控制预热并发数。

    Rollback: 停止预热脚本,等待缓存自然过期或回滚代码变更

  • 对 catalog-svc 实施限流:在网关层限制商品详情页请求速率,保护数据库

    Risk: 部分用户请求被拒绝,但可防止系统完全崩溃。需设置合理阈值。

    Rollback: 移除限流规则,恢复全量请求

  • 扩展数据库读副本:将读流量临时导向只读副本,减轻主库压力

    Risk: 副本可能存在复制延迟,导致数据不一致。需确保应用支持读写分离。

    Rollback: 切回主库,关闭副本连接

Customer impact

黑五期间,用户访问商品详情页时遇到高延迟(15 秒)或间歇性 503 错误,影响浏览和下单体验。预计影响所有通过首页和营销邮件进入的用户,约占当前流量的 80%。

Postmortem draft

Summary

09:00 UTC 黑五流量高峰期间,catalog-svc 因缓存雪崩导致数据库 CPU 100%,p99 延迟飙升至 15 秒,间歇性 503 错误。所有商品详情页请求绕过缓存直接查询数据库,影响用户浏览和下单。

Timeline (UTC)

[FILL IN]

Impact

  • 用户可见的 503 错误率 8%
  • p99 延迟 15 秒(基线 80ms)
  • 数据库 CPU 100% 持续 30 分钟
  • 影响所有商品详情页请求

Root Cause

缓存 TTL 统一设置为 7 小时,导致所有 catalog:item:* 键在 09:00 UTC 同时过期,黑五流量 12 倍于正常,引发缓存雪崩。

Detection

09:02 UTC 收到 DBHighCPU、CatalogSvcLatencyHigh、CheckoutErrorRate 告警。

Response

[FILL IN]

What Went Well

  • 告警及时,监控覆盖关键指标
  • Redis 缓存 miss 率指标帮助快速定位

What Went Poorly

  • 缓存 TTL 未设置抖动,导致同时过期
  • 未对黑五流量进行压测和容量规划
  • 缺乏缓存预热机制

Action Items

  • [ ] 为缓存 TTL 添加随机抖动,避免同时过期
  • [ ] 实现缓存预热脚本,在流量高峰前主动填充缓存
  • [ ] 对 catalog-svc 进行黑五流量压测
  • [ ] 增加数据库连接池监控和自动扩容

Follow-ups

  • P0为 catalog:item:* 缓存 TTL 添加随机抖动(如 7h ± 30min)服务所有者
  • P0实现缓存预热脚本,在流量高峰前主动填充热门商品缓存平台团队
  • P1对 catalog-svc 进行黑五流量压测,验证缓存雪崩场景QA 团队
  • P1增加数据库连接池使用率和查询队列长度的告警可观测性团队
  • P2评估引入分布式缓存或本地缓存层,减少对 Redis 的依赖架构团队
  • P2审查 Redis 驱逐策略和内存配置,确保缓存容量充足SRE 团队