4. 缓存穿透原理:请求绕过缓存直接查询数据库的成因与危害
缓存穿透是分布式缓存系统中破坏力最强的失效模式之一。当客户端请求的数据在缓存中不存在,且数据库中也不存在该数据时,每次请求都会绕过缓存直接穿透到数据库层。由于缓存层无法对该类请求进行拦截,大量并发请求会瞬间压向后端存储,导致数据库连接池耗尽、慢查询堆积,甚至引发级联雪崩。
4.1 缓存穿透的成因分析
缓存穿透并非缓存失效的常规场景,其核心成因可归纳为以下三类:
| 成因类别 | 具体描述 | 典型场景 |
|---|---|---|
| 恶意攻击 | 攻击者构造大量不存在的主键 ID(如负数、超大整数、随机字符串)发起请求,缓存层永远无法命中,请求直接穿透至数据库。 | 爬虫遍历不存在的商品 ID;刷接口攻击 |
| 业务逻辑缺陷 | 上游服务未对请求参数做合法性校验,导致非法 key 进入缓存查询流程,缓存 miss 后直接查询数据库。 | 前端传入空值或格式错误的用户 ID |
| 缓存与数据库数据不一致 | 数据库中被删除的数据,缓存中对应的 key 未被及时清理,后续请求查缓存 miss,查数据库也返回空,形成穿透。 | 后台删除商品后未同步删除缓存 key |
关键判断标准: 缓存穿透与缓存击穿的本质区别在于——击穿是缓存中存在的热点 key 过期,而穿透是缓存中根本不存在该 key,且数据库中也无对应记录。
4.2 缓存穿透的危害链
一次穿透请求的危害有限,但高并发场景下的穿透会形成以下危害链:
- 数据库连接耗尽: 应用层线程池中的每个线程都阻塞等待数据库查询,连接池快速被占满。
- 慢查询堆积: 数据库对不存在数据的查询无法利用索引覆盖,全表扫描导致 CPU 和 IO 飙升。
- 服务响应超时: 上游调用方因等待数据库响应而超时,触发重试机制,进一步放大流量。
- 级联雪崩: 数据库负载过高导致正常请求也变慢,缓存层因等待数据库响应而积压,最终整个服务集群不可用。
生产事故警示: 某电商平台在促销期间遭遇恶意攻击,攻击者遍历不存在的 SKU ID(从 1 到 10^8),导致数据库每秒承受超过 20 万次无效查询,主库 CPU 使用率瞬间飙升至 100%,核心商品查询接口超时率达 95%,最终触发全站降级。
4.3 缓存穿透的请求流程(SVG 架构图)
下图展示了正常请求与穿透请求在缓存层和数据库层的路径差异:
4.4 缓存穿透的量化影响
以下表格对比了正常请求与穿透请求在资源消耗上的差异:
| 指标维度 | 正常请求(缓存命中) | 穿透请求(缓存 Miss + 数据库空) | 性能差距 |
|---|---|---|---|
| 响应延迟 | 1~5 ms | 50~200 ms(数据库查询) | 10~40 倍 |
| 数据库连接占用 | 0 | 1 个连接 / 请求 | 无限放大 |
| 数据库 CPU 消耗 | 0 | 全表扫描,CPU 密集 | 极高 |
| 缓存层负载 | 正常读写 | 大量 Miss,无缓存写入 | 缓存层空转 |
| 系统吞吐量 | 10,000+ QPS | 500~1,000 QPS(受限于数据库) | 下降 90%+ |
4.5 缓存穿透与缓存击穿、缓存雪崩的对比
为了帮助理解缓存穿透在整个缓存失效体系中的位置,下表对比了三种常见失效模式:
| 失效模式 | 触发条件 | 影响范围 | 典型应对策略 |
|---|---|---|---|
| 缓存穿透 | 请求的数据在缓存和数据库中都不存在 | 单个 key 或一组不存在的 key | 布隆过滤器、缓存空对象、参数校验 |
| 缓存击穿 | 热点 key 在缓存中过期,大量并发请求同时访问 | 单个热点 key | 互斥锁、逻辑过期、永不过期 |
| 缓存雪崩 | 大量 key 同时过期或缓存节点宕机 | 整个缓存层或大量 key | 过期时间随机化、缓存集群高可用、限流降级 |
设计启示: 缓存穿透是所有缓存失效模式中最容易被忽视的一种,因为它不依赖缓存本身的过期机制,而是依赖业务数据的完整性。在搜索服务中,用户输入的搜索词可能包含大量无意义或恶意构造的词汇,缓存穿透的防御必须前置到接口层。