理解 Thundering Herd Problem:在分布式系统中驯服冲锋

发布: (2026年2月24日 GMT+8 23:21)
7 分钟阅读
原文: Dev.to

Source: Dev.to

惊群问题

想象一家热门商店在 上午 9 点准时 开门。数百名顾客在门外排队,随后同时冲进去,导致收银员不堪重负,现场一片混乱。
在分布式系统中,当 大量请求同时涌向同一个共享资源 时,也会出现同样的情况——这就是 惊群问题(Thundering Herd Problem)

Diagram

NORMAL OPERATION (Cache Hit)                  THUNDERING HERD (Cache Miss Stampede)
    Fast path                                             Failure path
┌─────────────┐                                         ┌─────────────┐
│   Clients   │                                         │   Clients   │
│ 10k users   │                                         │ 10k users   │
└──────┬──────┘                                         └──────┬──────┘
       │                                                    │
       ▼                                                    ▼
 ┌─────▼─────┐   Cache Hit   ┌──────────────┐            ▼
 │ App Server│◄──────────────│ Redis Cache  │   ┌──────▼──────┐
 │   Node 1 │               │ key=product1 │   │ 10k Cache   │
 └─────┬─────┘               │ TTL=60s      │   │   MISSES    │
       │                     └──────┬───────┘   └──────┬──────┘
       │               Cache Miss │                │
       ▼                            ▼                ▼
 ┌─────▼─────┐                 ┌──────────────┐ ┌──────────────┐
 │ App Server│                 │   Database   │ │   Database   │
 │   Node 2  │◄─── 1 Query ────│ 1 Query Only │ │ 10k Queries! │
 └─────┬─────┘                 │ Returns Data│ │ CPU=1000%    │
       │                     └──────┬───────┘ └──────────────┘
       │                            │                💥 OVERLOAD
       └──────────┬─────────────────┘

             ┌────▼────┐
             │Cache Set│ ← Serves all 10k clients
             └─────────┘

什么是 Thundering Herd Problem?

The Thundering Herd Problem occurs when numerous clients or processes simultaneously compete for the same shared resource (e.g., a database or cache). This creates a sudden traffic spike that overwhelms the system. Unlike a gradual load increase, the herd is synchronized burst—think of cache keys expiring at the exact same timestamp across millions of requests.

Source:

常见出现位置

组件典型场景
缓存系统常见的缓存条目同时失效,导致大量后端请求一次性触发。
数据库多个应用服务器在缓存未命中后同时冲击数据库。
负载均衡器故障发生时,请求涌向唯一健康的节点。
锁获取进程在关键区争抢互斥锁。

在典型的应用架构中:

  1. 客户端向应用服务器发起查询。
  2. 服务器首先检查 Redis(或其他缓存)。
  3. 缓存命中? 立即返回。
  4. 缓存未命中? 从数据库读取,重新填充缓存,然后返回。

实际案例:缓存过期突发

考虑 Netflix 推出一部热门新剧。数百万用户请求已缓存、TTL 为 60 秒 的剧集数据。当 TTL 失效时:

Normal:   Cache serves 10k req/s at ~1 ms latency
Expiry:  10k DB queries at ~100 ms each → 5‑10× overload

结果:数据库连接耗尽,延迟飙升至秒级,级联故障影响整个应用。类似的突发在印度 IPL 票务销售或黑色星期五电商抢购时也会出现。

时间线:同步缓存过期突发

正常流量激增 vs. 雷鸣羊群

方面正常流量激增雷鸣羊群
原因自然增长(营销、活动)同步事件(TTL 过期、定时任务)
模式逐步上升瞬时突发
影响自动伸缩能够应对即使扩容也会被压垮
持续时间分钟‑小时秒级(但破坏性极大)
关键区别可预测,分散可预测 同步化,将极小的脆弱窗口放大为故障

为什么在分布式系统中它是危险的

Clients → App → DB overload → Timeouts → Retries → More DB load → 💥
  • 放大效应 – 1 次缓存未命中 → N 次数据库查询(其中 N = 并发客户端数)。
  • 尾部延迟 – 最慢的数据库查询会阻塞所有请求。
  • 级联故障 – 过载的数据库导致应用变慢 → 更多超时 → 重试风暴。
  • 自动扩容滞后 – 突发流量持续时间太短,无法让新实例及时启动。
  • 在多区域部署中,一个区域的抢占式请求会在全球范围内产生连锁反应。

系统影响细分

CPU 过载

  • 突然的线程爆炸冲击调度器;上下文切换激增。

数据库压力

  • 连接池耗尽;查询队列膨胀 → 超时连锁。

缓存失效

  • 在流量冲击期间变得毫无用处——甚至比根本没有缓存更糟!

延迟爆炸

  • P99 延迟可能飙升 100 倍,导致用户放弃会话。

防护技术

  1. Stale‑While‑Revalidating – 只有一个请求刷新缓存;其他请求返回陈旧数据并复用结果。
  2. Mutex (Distributed Lock) – 使用锁(例如 Redis SETNX)确保只有一个请求访问数据库。
  3. Jitter on TTLTTL = base + random(0, maxJitter),避免同步失效。
  4. Probabilistic Early Computation – 根据访问频率或接近失效时间提前刷新热点键。
  5. Rate Limiting – 限制每个键/用户的请求次数,防止后端过载。
  6. Circuit Breaker / Bulkhead – 隔离故障组件并在羊群效应传播前削减负载。
  7. Cache Warm‑up – 在关键键过期前预先填充(例如在低流量时段)。

缓存预热

  • 在流量激增或部署前预加载热点键。

真实故障案例:Facebook 2010 年的缓存踩踏导致数小时才能恢复——
The Day Facebook Died – A Cache‑Stampede Horror Story That Changed Tech Forever

最后思考

惊群效应(Thundering Herd)把“大规模工作”变成没有适当防护的宕机。
掌握这些模式——错峰 TTL + 合并 + 退避

下次缓存过期时,请记住:单只牛还行,成群的牛致命。

0 浏览
Back to Blog

相关文章

阅读更多 »

理解 Golang Worker Pattern

生成工作线程 intNumWorkers := 3 for w := 0; w < intNumWorkers; w++ { go InsertStudentDatactx, tx, insertedStudentJobs, insertedStudentjobsErrCh } go func I...