1 缓存穿透的基本概念
1.1 定义与表现形式
1.1.1 请求如何绕过缓存
缓存穿透描述的是:当客户端或上游服务按某个 key 查询数据时,该 key 在缓存中不存在(或缓存已被清空、未写入),系统便直接回源到后端数据源(常见为数据库或其他存储),再将结果写回缓存。若所请求的数据本身长期“不存在”,或请求被持续构造为大量缓存中没有的 key,就会使大量查询反复走回源链路,形成“缓存形同失效”的现象。
1.1.2 与“缓存击穿/缓存雪崩”的区别
缓存击穿通常指某个热点 key 过期或被删除后,大量并发同时回源,瞬时把后端打满;核心在于“少量热点 key 在失效瞬间产生集中回源”。缓存雪崩则强调大量 key 在同一时间段(例如统一 TTL 到期)失效,引发大面积回源,表现为“时间维度的集中”。缓存穿透则更侧重于“空间维度的无效性”:请求所涉及的 key 本来就不在缓存或不在数据源中,导致无法通过命中缓存自然消化压力,因而持续绕过缓存。
1.2 常见触发场景
1.2.1 不存在数据的高频查询
例如客户端反复查询某类 id 在业务上本应不存在的对象,或查询结果确实为空且系统没有对“空结果”进行缓存。由于缓存中没有对应条目,请求每次都会回源,产生稳定但放大的后端负载。对用户而言可能呈现为响应变慢、错误率上升;对系统而言是后端吞吐被无效请求占用。
1.2.2 随机/恶意构造 key
当缺少对 key 的合法性约束,攻击者可能构造大量随机参数来探测或拖垮系统。即便数据源查不到,相同的“无效 key 查询”仍会不断回源。此类行为在工程上常被视为一种“放大器”:缓存未能阻断请求,后端被迫重复执行同样的查找逻辑,从而引发资源耗尽。
1.2.3 缓存未命中策略与回源链路导致的放大
有些系统在未命中时会立即回源、并在回源后再写入缓存;但如果回源链路较长、回源开关或兜底策略不足,在高并发下未命中概率上升,就会把负载进一步扩大。例如:回源没有并发合并(请求合并),没有设置合理超时,或在写缓存失败时没有降级,都会让穿透问题更容易演化为大规模性能故障。
1.3 指标与定位思路
1.3.1 缓存命中率与回源次数
定位通常从“命中率异常下降”和“回源次数异常上升”入手。若命中率长期偏低且与业务实际访问模式不匹配,且回源次数随请求增长呈线性或超线性增加,往往意味着存在大量无效 key 或缓存治理不足。
1.3.2 后端QPS与延迟升高特征
穿透导致的回源会挤占后端资源,使数据库或下游服务 QPS 持续升高,延迟上升,并可能引发连接池耗尽、慢查询堆积等现象。与击穿相比,穿透往往不是短时间的突刺,而更可能伴随稳定或持续的回源压力。
1.3.3 日志与链路追踪的典型信号
链路追踪中可观察到:同一类查询路径在缓存层标记为未命中后,总是进入回源,并且回源结果偏向空值或“未找到”。日志层面可能出现大量“not found”“empty result”“fallback to db”等记录,并伴随特定参数模式异常(例如参数分布过宽、包含大量不符合格式的 id)。
2 影响与风险
2.1 对后端系统的压力
2.1.1 数据库/存储的连接与CPU占用
当无效 key 被持续回源,数据库需要反复执行查询、索引遍历或表扫描(视具体实现而定)。连接数可能被迅速占满,CPU 被查询计算和上下文切换消耗,进而拉高等待时间。即便每次查询成本不高,累积也会形成明显的资源瓶颈。
2.1.2 下游服务雪上加霜(级联故障)
很多系统不是“数据库直连”,而是存在多级服务依赖:缓存未命中后可能还要走鉴权、聚合、二次查表或调用外部服务。穿透造成的回源放大,会让这些下游同样承压,最终可能触发超时、重试放大以及熔断机制的连锁反应,导致整体服务能力下降。
2.2 对业务可用性的影响
2.2.1 超时与错误率上升
回源延迟增长后,客户端或上游请求更可能触发超时,返回错误码增多。对于前端或调用方,表现为响应变慢、失败重试次数增加,进一步加剧请求量与压力。
2.2.2 资源耗尽与服务降级触发
当后端资源接近上限(连接池满、线程池耗尽、队列积压),系统通常会启动降级或限流。若降级策略设计不足,可能导致关键业务也无法获得足够资源,形成“无效请求挤占有效请求”的不公平现象。
2.3 安全与滥用风险(轻量概述)
2.3.1 构造请求导致的放大效应
在安全层面,缺乏参数校验和保护时,攻击者可用大量无效请求放大对后端的影响。此类问题常不需要特别复杂的 payload,只要能触发回源即可。
2.3.2 缺乏校验带来的“无穷回源”
若系统对非法或不存在 key 没有有效拦截机制,并且对空结果没有缓存或限制回源频率,就会出现理论上的“无穷回源”。这在工程实践中经常被视为高优先级风险点。
3 缓存穿透的成因分析
3.1 缓存策略缺陷
3.1.1 未缓存空值(空结果回源)
当查询结果为空、或数据源明确表示“没有该记录”时,如果系统没有把这种结论写入缓存(例如写入短 TTL 的空标记),那么后续请求仍会反复回源,形成持续穿透。
3.1.2 TTL设置不当导致的持续回源
TTL 过短可能使缓存频繁失效;TTL 过长则可能在数据更新后带来不一致。对于穿透而言,更危险的是:无效结论没有得到合适的 TTL 管理,导致空结果在缓存层得不到复用。
3.1.3 回源开关与兜底策略缺失
例如当缓存未命中时默认回源,且没有对回源并发、回源超时、回源限流做控制,会把风险直接暴露给后端。此外,若写缓存失败没有合理处理,可能导致同一无效 key 永久无法进入缓存治理闭环。
3.2 数据层与缓存层不一致
3.2.1 写入失败或延迟导致短暂缺口
在数据写入与缓存更新之间存在时间窗口时,可能出现短暂的“缓存没有、数据也尚未可查”的状态。正常情况下这只会影响少量请求,但在高并发或重试机制下,缺口可能被放大成可观的回源压力。
3.2.2 删除/更新未同步缓存状态
若删除或更新操作未正确同步到缓存层,缓存可能仍保留旧值或缺失新值。对穿透而言,最直接的后果是:系统判断为未命中或返回空,随后再次回源,反复产生“看似不存在”的查询结果。
3.3 高并发与边界条件
3.3.1 热点之外的“冷门随机键”
即便没有明显热点,冷门 key 也可能因为请求量分散而带来高未命中率。若其中包含大量不存在键,穿透效应就会被持续触发。
3.3.2 瞬时突发流量触发穿透放大
当流量在短时间内突增(例如活动、联动推荐),未命中的比例可能立刻抬升。若系统缺少限流、熔断与请求合并,就会在突发窗口把后端推向临界状态。
4 防护与治理方法
4.1 空值缓存与不存在值缓存
4.1.1 缓存空结果的策略选择
常见做法是:当回源确认结果不存在时,向缓存写入“空标记”或短暂的不存在值条目,以阻止后续重复回源。该策略的关键在于写入标记要能区分真实值与空值,避免业务误用。
4.1.2 TTL与刷新机制建议
空值缓存通常配置较短 TTL,使其在合理时间内反映数据的潜在变更。例如数据后续被写入或修复后,空缓存到期后可以再次回源验证。刷新机制方面,可结合异步更新或定期校验,确保空值不会长期“封死”正确数据。
4.2 布隆过滤器等近似集合校验
4.2.1 工作原理与适用边界
布隆过滤器可用于判断某 key 可能存在还是一定不存在:若过滤器判定“不可能存在”,即可直接拒绝回源,从而减少穿透范围。由于是近似结构,适合用于“宁可多拒绝一些也不要无谓回源”的场景;当必须严格准确时,需要谨慎评估误判影响。
4.2.2 误判率与工程参数
布隆过滤器存在误判率可控的问题:过滤器容量、哈希函数数量、位图大小共同影响误判概率。工程上通常依据可接受的误判风险、可估算的 key 数量与更新频率选择参数,并在压测中验证在高并发下的稳定性。
4.3 参数校验与键空间治理
4.3.1 输入合法性校验(格式、范围、黑白名单)
在进入缓存查询前,对参数格式、数值范围、必要字段进行校验,可快速拒绝明显无效请求,缩小穿透来源。对于重要业务字段,使用白名单或枚举映射可进一步降低随机探测的空间。
4.3.2 关键字段约束与防止无意义请求
例如对 id 设置合理区间、对组合参数进行一致性校验,避免组合出大量不可能匹配的数据形态。对日志与告警也应能反向识别异常参数分布,以便形成持续治理闭环。
4.4 限流、熔断与降级
4.4.1 限流策略位置与粒度
限流可放在网关、应用层或缓存层之前。粒度上可按用户、接口、key 类别或服务实例划分。对穿透而言,按 key 或参数类别进行策略更容易控制“无效请求的扩散”,并减少有效请求被淹没的概率。
4.4.2 熔断触发条件与回退路径
熔断通常依据后端错误率、超时比例、资源占用等信号触发。当回源路径不稳定时,系统应快速切换到回退路径,例如返回兜底结果、只读降级或短路失败,避免持续回源造成更大连锁故障。
4.4.1 和 4.5.1 在工程上需协同考虑:限流能限制进入回源的量,熔断能在回源不可用时阻断链路;两者叠加更能提升系统韧性。
4.4.1 与缓存策略的协同关系
当空值缓存与过滤层已经减少了无效回源,限流与熔断可更宽松;反之若治理不足,应提高拦截与短路力度,避免后端被拖垮。协同的目标是让“绝大多数请求在前置环节被消化”,而不是把调控压力留给后端。
4.5 回源控制与并发合并
4.5.1 单飞(SingleFlight)/请求合并
单飞机制允许同一 key 的并发回源请求合并为一次实际查询,其余请求等待结果或复用缓存结果。这不仅能缓解击穿,也能在穿透场景中减少同一不存在值的重复回源频次,降低后端的重复计算。
4.5.2 失败重试与超时策略
合理的超时与重试策略能避免因瞬时抖动导致的重试放大。对回源失败或空结果,通常应限制重试次数,并确保在回退路径上有清晰的响应策略,以免系统在故障窗口反复进入昂贵链路。
5 工程实践方案
5.1 典型架构与组件组合
5.1.1 缓存层与后端回源链路
典型流程包括:先查缓存,命中则直接返回;未命中则进行参数校验,再进入回源查询;回源结果写入缓存(包含空值标记的策略)。为保证稳定性,回源链路应设置并发限制、超时边界与失败兜底。
5.1.2 过滤层(如布隆过滤器)的位置
过滤层通常位于应用查询逻辑的更前端:当过滤器判定 key 一定不存在时,直接返回空结果或默认值,避免回源。若采用“可能存在则回源”的策略,过滤器需要与数据源的构建流程、同步周期保持一致,减少误判带来的业务影响。
5.2 策略设计与参数调优
5.2.1 TTL、空值缓存时长与一致性取舍
空值 TTL 太短会使无效请求再次频繁回源;太长可能在数据被补齐后延迟生效。工程上通常在“容忍的回源成本”和“可接受的一致性延迟”之间平衡,并结合数据变更频率进行动态或分级配置。
5.2.2 布隆过滤器规模与误判控制
过滤器位图大小与哈希数量决定存储开销与误判率。通常通过对 key 集合规模的估算进行初始配置,再结合实际运行的误判与回源统计做微调。更新策略上可采用定期重建或分层结构,以兼顾新增数据。
5.2.3 限流阈值与降级优先级
限流阈值需考虑峰值流量、回源成本与系统可承受的后端压力。降级优先级可按业务重要性排序:例如非关键查询在回源不可用时返回默认值或降采样,而关键接口保留资源并触发更严格的保护策略。
5.3 灰度发布与验证方法
5.3.1 压测用例设计(随机键/不存在键)
压测应覆盖不存在键的高频场景、随机键探测场景,以及正常数据与异常数据并发混合的场景。通过对比开启空值缓存、启用过滤器、以及启用单飞机制后的指标变化,验证穿透治理是否有效。
5.3.2 监控看板与报警规则
监控建议重点关注缓存命中率、回源次数、回源延迟、后端错误率、超时比例,以及回源链路的并发与队列积压情况。报警规则可采用阈值与趋势结合:例如命中率快速下跌且回源次数同步上升时触发告警,并在可选的情况下自动联动限流或熔断。
6 运维与故障应对
6.1 监控告警与自动化处置
6.1.1 命中率阈值与回源突增告警
当命中率持续下降且回源请求出现突增,通常表明缓存治理失效或请求模式异常。告警应区分“真实业务量增长”与“无效请求放大”,可结合请求参数分布与回源结果为空的比例进行判断。
6.1.2 后端健康度与熔断联动
当后端健康度指标显示过载趋势(例如慢查询积压、连接耗尽风险升高),系统可联动触发熔断或更严格的限流策略。联动目标是让系统在可控范围内先“保住存量”,再等待治理策略生效。
6.2 典型排障流程
6.2.1 从缓存指标到回源定位
首先检查缓存命中率曲线与未命中原因分布,确认是否存在特定接口或 key 类型导致未命中异常。随后查看回源链路耗时、错误率、以及回源结果为空的比例,以定位穿透的主导来源。
6.2.2 从请求分布到键空间治理
在确认是无效 key 导致的穿透后,需要分析请求参数分布是否异常宽泛,以及是否存在批量随机探测迹象。进一步对 key 生成规则、输入校验、以及黑白名单策略进行核对,并在发现关键缺口时进行快速修补与复测。
6.3 经验教训与“别让缓存替你背锅”
6.3.1 常见误区(只做热键、不管冷键)
一些系统只针对热点数据优化缓存,却忽略冷门或不存在数据的查询同样会引发回源。穿透治理通常不是“只缓存热点”即可解决,而是需要覆盖空结果与无效参数的全链路策略。
6.3.2 团队协作与文档化标准
缓存治理涉及产品、后端、运维与安全等多个角色。建议形成统一的键命名规范、空值缓存规范、TTL 与回源策略文档,以及压测与上线验收标准,减少“上线后才发现治理缺口”的成本。
7 相关概念与延伸阅读
7.1 缓存击穿
缓存击穿强调热点 key 失效导致的集中回源,常见应对包括请求合并、延迟双删、热点重建等思路。
7.2 缓存雪崩
缓存雪崩关注大量 key 在相近时间失效引发的大面积回源,治理手段通常包含随机 TTL、分批失效、容量预热与降级策略等。
7.3 缓存一致性与失效策略
缓存一致性涉及写入顺序、更新传播与失效机制。合理的失效策略能降低错误命中或空值误缓存带来的影响,并提升整体可预测性。
7.4 常用中间件/框架中的缓存治理能力(概念性概述)
许多中间件或框架提供缓存抽象、参数校验钩子、限流熔断集成、以及缓存刷新与失效回调等能力。选型时可关注其是否支持空值缓存、请求合并与可观测性能力,以便系统化应对穿透风险。