慢请求别急着先查慢 SQL(Redis 缓存雪崩把数据库打满)
2026/6/3 18:29:28 网站建设 项目流程

api-home-x 的异常第一眼很像数据库扛不住:接口 P99 上升,数据库 CPU 冲高,active connections 从 120 涨到 780。

但这类问题最容易误判。数据库被打满只是结果,真正要确认的是:它是 SQL 变慢导致连接堆积,还是缓存失效后回源请求突然放大,把原本正常的 SQL 执行量打爆。

这次的关键判断点不在某一条慢 SQL,而在三条曲线是否同一时间拐头:缓存命中率、回源 QPS、数据库连接数。

现场最有价值的不是 CPU,而是命中率

故障窗口里,入口流量有活动带来的上升,但没有到完全解释数据库压力的程度。先看缓存侧指标:

redis get QPS: 90k -> 42k cache.hit.ratio: 96% -> 61% cache.miss.count: 与 db.query.count 同步上升 TTL 分布: 大量 key 集中在 10:00 前后过期

这组数据基本把方向从“Redis 变慢”推到了“Redis 没命中”。redis get QPS下降,不是因为请求少了,而是大量请求在 miss 后直接进入回源路径。命中率从 96% 掉到 61%,对首页这种高频接口来说,意味着原本被缓存挡住的请求被成批放到了数据库上。

更危险的是 TTL 分布。大量 key 在 10:00 前后集中失效,这不是单个热点 key 的击穿,而更接近批量过期造成的雪崩;同时,活动页配置 keyredis:key:home:activity:x又存在单 key miss 后并发重建的问题,所以现场是“雪崩里夹着击穿”。

为什么不是数据库自己变慢

数据库指标看起来很吓人:

db active connections: 120 -> 780 db CPU: 35% -> 92% api-home-x 相关 SQL: 单次耗时未明显变慢 Rows_examined: 稳定 执行计划: 稳定

如果是 SQL 计划退化,通常会看到单次 SQL 耗时、扫描行数、临时表、排序或锁等待出现明显变化。但这里Rows_examined和执行计划稳定,说明 SQL 本身没有突然变成另一个执行路径。

真正变化的是请求量。缓存 miss 和数据库 query count 同步上升,active connections 随之被推高。数据库 CPU 到 92%,不是因为每条 SQL 更重,而是大量原本应该命中缓存的请求同时回源。

这个结论很关键。处理 SQL 慢和处理缓存雪崩是两套动作:前者看索引、执行计划和锁;后者要先挡住回源放大,否则即使临时扩数据库,也可能只是把故障窗口延后几分钟。

放大链路:TTL 集中过期 + 无互斥重建

这次链路可以简化成四步:

  1. 活动开始前后,大量首页相关缓存 key 的 TTL 集中到期。
  2. api-home-x读缓存时命中率快速下降。
  3. 缓存重建没有互斥保护,同一个 key miss 后并发请求同时查询数据库。
  4. 数据库连接数和 CPU 被回源请求推满,接口延迟继续升高,进一步放大排队。

这里有两个边界需要区分。

第一,它不是典型 Redis hot key 主导。hot key 常见表现是某个分片、某个 key 或某类命令访问极端倾斜,Redis 节点 CPU、网络或代理层 key 分布会先异常。本案里 Redis 单分片没有明显倾斜,问题核心是 miss 后的回源。

第二,它也不是单纯缓存穿透。穿透通常是大量不存在的数据持续打到后端,需要看空值缓存、参数校验或布隆过滤器。本案有明确 TTL 集中过期和活动页 key 重建并发,主线仍是击穿与雪崩。

止血要先控制回源,而不是只扩数据库

应急阶段的优先级是把数据库从并发回源里解出来:

动作目的验证指标
对 api-home-x 做临时限流或降级兜底降低回源并发峰值DB active connections 回落
对活动页核心 key 做临时预热和续期恢复高频 key 命中cache.hit.ratio 回升
给缓存重建加互斥保护避免同一 key 并发回源单 key miss 后 DB query 不再成倍放大
对非核心模块使用短期兜底数据保住首页主链路接口 P99 和超时率下降

根治阶段不能只写“缓存加随机 TTL”这么一句。至少要补齐三类机制:

  • TTL 随机化:同一批缓存不要在同一时间点过期,尤其是活动页、首页、配置类 key。
  • 重建保护:热点 key miss 后只允许一个线程或一个实例回源重建,其余请求等待、复用旧值或走降级。
  • 热点预热与续期:活动开始前预热核心 key,活动中对高频 key 做续期或异步刷新,避免自然过期撞上流量峰值。

修复后的验证口径也要闭环:命中率恢复到稳定水位,miss count 与 DB query count 不再同向暴涨,DB active connections 回到基线附近,api-home-xP99 和超时率同步下降。

常用排查命令

下面这组命令适合排查缓存击穿、雪崩和数据库被回源打满的问题。不要只看单点输出,要把时间线对齐。

redis-cli -h <host> -p <port> info stats

keyspace_hitskeyspace_missesinstantaneous_ops_per_sec,确认命中率和 miss 是否在故障窗口突变。

redis-cli -h <host> -p <port> info commandstats

getmgetset等命令调用量和耗时,判断是读缓存变慢,还是 miss 后回源变多。

redis-cli -h <host> -p <port> --hotkeys redis-cli -h <host> -p <port> --bigkeys

谨慎在线执行,主要用于排除 hot key、big key 或异常数据结构导致的 Redis 服务端瓶颈。

redis-cli -h <host> -p <port> slowlog get 20

看是否存在慢命令。如果 Redis 慢日志干净,而 DB 压力同步上升,更应该怀疑回源放大。

SHOW FULL PROCESSLIST; EXPLAIN ANALYZE <sql>; SHOW ENGINE INNODB STATUS\G

看数据库端连接是否大量卡在同类查询上,并确认 SQL 执行计划、扫描行数、锁等待是否真的变化。

grep 'api-home-x' access.log | awk '{print $NF}' | sort -n | tail

抽样看接口耗时分布。入口 P99、缓存 miss、DB 连接数三者同向变化时,排查方向会比单看 CPU 更稳。

复盘时要追问的 4 个问题

这类故障的直接原因是缓存批量失效后并发回源,数据库被请求量放大打满。

根因不只是 TTL 集中,而是缺少缓存重建互斥、热点 key 预热续期和回源预算。TTL 是触发器,防护缺口才是故障能扩大的原因。

后续复盘可以重点追问四件事:

  • 核心缓存 key 的 TTL 是否集中,是否按业务批次一起写入。
  • miss 后是否允许并发回源,是否有互斥、请求合并或旧值兜底。
  • 活动、发布、任务开始前是否有缓存预热和命中率看板。
  • 缓存层是否配置了回源限流,数据库是否有连接池等待、active connections、query count 的联动告警。

缓存不是数据库的加速插件,而是容量模型的一部分。只要缓存失效能把请求原样放回数据库,就必须按“后端能承受多少回源”来设计,而不是按“平时命中率很高”来安慰自己。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询