缓存穿透是指查询既不在缓存也不在数据库中存在的数据,导致请求直击数据库;高并发下会迅速耗尽mysql连接与资源,引发拒绝服务或崩溃,需通过布隆过滤器、空值缓存、参数校验等组合手段防御。

缓存穿透本身不会直接让 MySQL 宕机,但高并发下大量穿透请求会迅速耗尽数据库连接、CPU 和 IO 资源,最终触发 MySQL 主动拒绝连接或进程崩溃——这是典型的“压垮”而非“击穿”。
为什么穿透请求能打垮 MySQL
MySQL 单机连接数默认一般为 151(max_connections),实际稳定并发写入能力常低于 2000 QPS;而 Redis 轻松支撑 5w+ QPS。一旦缓存层对某个非法 id(比如 "user:999999999")不设防,1000 个并发请求全落到 MySQL,每个查询都走完整解析 → 优化 → 引擎层 → 磁盘 IO 流程,几秒内就能让 Threads_connected 拉满、innodb_row_lock_waits 飙升、Slow_queries 暴增。
更隐蔽的是:这类查询往往没走索引(如用字符串 ID 查询 bigint 字段),触发全表扫描,单条就卡住几十毫秒,进一步放大阻塞效应。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
如何快速定位是否是缓存穿透引发的问题
- 查 Redis 监控:看
keyspace_hits/keyspace_misses比值是否骤降(比如从 99% 掉到 30%),同时expired_keys无明显增长 → 排除雪崩/击穿 - 查 MySQL 慢日志:
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log,重点关注返回0 rows但耗时 >100ms 的语句,尤其是 where 条件含非常规值(id = -1、id = "abc"、id > 10000000) - 查应用层日志:搜关键词
"not found"或"null result",如果同一秒内高频出现且参数高度离散(如user_id=123456789、user_id=123456790…),基本可锁定穿透 - 临时在 DAO 层加埋点:统计
select * from user where id = ?返回空的频次,突增即为证据
修复必须分两步:先拦住流量,再补缓存逻辑
只加空值缓存(SET user:999999999 "" EX 60)在面对恶意枚举时会撑爆 Redis 内存;只加参数校验又挡不住绕过网关的直连请求。必须组合落地:
- 前置过滤:在 Nginx 或 API 网关层用正则拦截明显非法
id(如负数、超长数字、非数字字符),返回400不进业务逻辑 - 布隆过滤器兜底:将所有合法
user_id的布隆位图加载进 Redis,查询前先Bloom.exists user_bf user:123456789,0则直接返回404 - 空值缓存收缩:仅对确认存在的业务主键(如
user_id、order_no)才缓存空结果,且过期时间严格控制在60秒内,避免长期占内存 - 拒绝重试机制:客户端收到
404后禁止自动重试,服务端对同一非法id在 1 分钟内最多响应 3 次,其余直接限流
真正难的不是加布隆过滤器,而是保证它的数据一致性——新增用户后必须同步更新布隆位图,否则会造成误拒。这个同步链路一旦断裂,比穿透还难排查。










