转载说明:本文整理转载自开源项目 PHP-Interview-QA(Apache-2.0 License,作者 @colinlet),感谢开源分享,版权归原作者所有。
一句话结论
PHP-FPM 下 Singleflight 跨进程不可行;单 Key 并发 ≤500 用互斥锁兜底;并发 >500 且可接受短暂旧值用逻辑过期。
业务场景
电商大促期间热点商品详情页:
- 热点 Key 峰值并发:单 Key 3000 并发请求/s(如某明星同款商品上架瞬间)
- DB 承载上限:500 QPS(正常水位,击穿后瞬间请求全部透传则超限)
- PHP-FPM 部署:100 Worker × 3 台 = 300 Worker 实例
- SLA:99.9%,P99 响应 ≤100ms
- 一致性约束:允许返回旧数据不超过 500ms(用户基本无感知)
体系讲解
缓存击穿的成因
热点 Key 在高并发下恰好过期,导致大量请求同时穿透到 DB。与缓存雪崩(大批 Key 同时过期)不同,击穿通常集中在单个热点 Key,瞬时 DB 压力可达平时的数百倍。
三种防护方案对比
| 方案 | 核心机制 | PHP-FPM 可行性 | 并发控制效果 | 响应时延影响 | 返回旧值 |
|---|---|---|---|---|---|
| Singleflight | 进程内合并并发请求,仅 1 个协程查 DB | ⚠️ 仅限单进程(Swoole/Roadrunner),FPM 跨进程不可行 | 极好(进程内 N→1) | 无额外延迟 | 否 |
| 互斥锁(Redis SETNX) | 分布式锁,仅 1 个 Worker 查 DB,其余等待或降级 | ✅ 完全兼容 | 好(全局 N→1) | 等待期增加延迟 | 可选(降级旧值) |
| 逻辑过期 | 不设 Redis TTL,value 中存 expire_at,过期后异步重建,同时返回旧值 | ✅ 完全兼容 | 好(重建请求 N→1) | 无阻塞,延迟不受影响 | 是(短暂旧值) |
Singleflight 在 PHP-FPM 下为何不可行
Singleflight 的核心是在同一进程内用 Map 记录"正在飞行中"的请求,后续重复请求复用第一个的结果。PHP-FPM 下每个 Worker 是独立进程,进程间不共享内存,300 个 Worker 各自独立发起 DB 查询,无法合并。在 Swoole 协程或 Roadrunner 下,同一 Worker 进程内的协程才能共享 Singleflight Map,有效范围仅限单进程。
逻辑过期方案决策流程
flowchart LR
R[请求到来] --> A{缓存存在?}
A -->|不存在| B[互斥锁重建<br>首次冷启动]
A -->|存在| C{expire_at 过期?}
C -->|未过期| D[直接返回]
C -->|过期| E{能抢到重建锁?}
E -->|是| F[异步触发重建<br>当前请求返回旧值]
E -->|否| G[直接返回旧值<br>等待重建完成]本场景选型
单 Key 并发 3000/s,允许旧值 500ms:选逻辑过期。
- 互斥锁在 3000 并发下,299 个未抢到锁的 Worker 需轮询等待,DB 重建期间 P99 劣化 100–200ms,触碰 100ms SLA 红线
- 逻辑过期下所有 Worker 立即返回旧值(<500ms 约束内),仅 1 个 Worker 异步触发重建,DB 压力为 1 QPS
- 代价:短暂(重建期间约 100–300ms)返回旧价格,业务已接受此约束
若业务不允许返回旧值(如库存强一致),则选互斥锁 + 降级返回"稍后再试"而非旧值。
并发量分级选型
| 单 Key 并发 | 可接受旧值 | 推荐方案 |
|---|---|---|
| < 100 | 任意 | 互斥锁(轮询等待,影响小) |
| 100–500 | 否 | 互斥锁 + 降级兜底值 |
| > 500 | 是 | 逻辑过期(异步重建) |
| > 500 | 否 | 降级熔断(直接返回错误/静态兜底页) |
考察意图
考察候选人是否理解 Singleflight 在 PHP-FPM 下的不可用边界,能否根据并发量和业务对旧值的容忍度量化选型,而非背"用互斥锁防击穿"的八股答案。
追问链
Q1:互斥锁的 EX(锁超时)应设多大?设置不合理会怎样?
Q2:逻辑过期方案,如何防止多个 Worker 同时触发异步重建?
Q3:逻辑过期方案,重建任务执行失败(如 DB 超时)怎么处理?
Q4:Singleflight 在 Swoole 场景下如何实现?
Q5:大促开始前如何预防热点 Key 击穿?
完整答案/易错点/代码见「学编程拿offer」公众号,回复「缓存击穿」获取
本文共 910 个字数,平均阅读时长 ≈ 3分钟
评论