当前位置:首页 > 文章列表 > 数据库 > Redis > 缓存穿透治理:空值、布隆过滤器与回源限流组合

缓存穿透治理:空值、布隆过滤器与回源限流组合

来源:17golang原创 2026-10-08 12:14:22 0浏览 收藏

接口突然出现一批从未存在过的商品 ID,缓存查不到,数据库却被重复查询,这就是典型的缓存穿透。比较稳妥的组合是:先用 Bloom Filter 拦截“确定不存在”的键,再用短 TTL 空值缓存吸收重复 miss,最后把真正可能存在的请求放进原子限流器。三道防线各自负责不同阶段,任何一道失效时仍有下一层兜底。

Bloom Filter 只负责快速排除,空值缓存负责记住近期的不存在结果,回源限流负责保护数据库;只有查到真实数据后,才写入正常缓存。
要点速览
  • 过滤器返回不存在时直接结束,返回可能存在时不能跳过真实查询。
  • 空值缓存要有短 TTL,并与正常数据使用清晰的值域区分。
  • 限流计数和过期时间要原子化,指标要能区分三种拦截结果。

先把三道防线放在正确位置

以 product:{id} 作为统一键,读取顺序应是“请求规范化 → Bloom Filter → Redis 正向/空值缓存 → 回源限流 → 数据库”。Redis 官方文档说明 Bloom Filter 可以用很小空间判断元素是否可能存在:返回否定时可以确定不存在,返回肯定时仍可能是假阳性。因此它适合做前置筛选,不能替代商品表。

建议把值域分成真实 JSON 和固定空值标记,例如 __MISS__。空值 TTL 可以从 30~120 秒起步,正向缓存则按商品变更频率设置;TTL 不是越长越好,重点是避免一个短时不存在结果永久阻断后续新数据。

Redis 缓存穿透治理结构说明图,展示 Bloom Filter、正向缓存、空值缓存、限流器和数据库的请求边界
图1:缓存穿透的分层结构说明图,展示三道防线与数据库回源边界;这是静态说明图,不是运行截图。

空值缓存与 Bloom Filter 要互相补位

Bloom Filter 适合挡住大量随机、长期无效的 ID;空值缓存适合吸收同一个无效 ID 被重复请求的情况。两者不能简单二选一:过滤器容量和误判率需要按有效 ID 数量估算,过滤器返回“可能存在”时仍要查 Redis 和数据库;数据库确认不存在后,再写入空值标记,让下一次请求快速结束。

写入新商品或恢复被删除商品时,要同步更新过滤器和缓存策略。删除商品时不要试图从普通 Bloom Filter 中强行删除,若业务需要频繁删除,可评估支持删除的 Cuckoo Filter,或者通过重建过滤器解决长期漂移。

回源限流用原子计数保护数据库

过滤器的“可能存在”结果仍可能把大量请求送到数据库,所以回源入口还需要按 IP、用户或资源维度做限制。下面的固定窗口示例用 Lua 把计数和首次过期放在一次执行中;生产环境也可以根据突发流量改成令牌桶。阈值只是示例,应结合数据库连接池、查询耗时和正常流量压测调整。

var allowSource = redis.NewScript(`
-- 计数与过期必须一次完成,避免并发下留下永不过期的键
local n = redis.call("INCR", KEYS[1])
if n == 1 then
  redis.call("EXPIRE", KEYS[1], ARGV[1])
end
-- 返回 1 表示本次允许回源,0 表示超过窗口
if n 

真正的读取函数还要区分三类结果:Bloom Filter 明确否定时返回 404;空值标记命中时返回 404;只有通过限流的“可能存在”请求才查库。查到实体写入正向缓存,查不到写入短 TTL 空值。若同一 Go 实例内仍有并发 miss,可再用 singleflight 合并一次查询,但它不能替代跨实例的 Redis 限流。

用指标确认组合没有变成新瓶颈

上线后不要只看整体缓存命中率,至少拆出 Bloom 拒绝数、空值命中数、正向命中数、回源放行数、限流拒绝数、数据库不存在数和限流器错误数。Redis 官方资料也强调,限流需要集中存储才能让多个服务实例共享同一配额;如果每个进程只维护本地计数,流量经过负载均衡后仍会绕过限制。

现象优先检查处理方向
Bloom 拒绝很多数据同步与过滤器容量确认是否误把新数据漏写
空值命中很多非法 ID 来源与 TTL治理调用方重试,避免 TTL 过长
回源限流很多窗口、维度和突发形态返回 429 或降级,不直接放开数据库
Redis 出错故障模式与连接池限制回源并告警,避免“缓存故障变数据库故障”
Redis 缓存穿透治理观测说明图,展示 Bloom 拒绝、空值命中、回源限流和数据库结果的指标分流
图2:指标分流说明图,把请求结果拆成过滤、缓存、限流和回源四类,便于定位瓶颈;这是静态说明图。

常见问题

Bloom Filter 返回存在就一定能查到数据吗?

不一定。Bloom Filter 允许假阳性,所以返回存在只能说明“可能存在”,最终结果仍由缓存和数据库确认。

空值缓存为什么不能设置很长时间?

因为暂时不存在的资源可能随后被创建,过长 TTL 会让新数据在空值过期前持续返回不存在。

固定窗口限流够不够用?

低复杂度接口通常够用;如果突发流量明显或窗口边界造成双倍突发,应改用滑动窗口或令牌桶,并继续保持原子执行。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
sql.DB 为什么不是一条连接,池参数应按什么容量估算sql.DB 为什么不是一条连接,池参数应按什么容量估算
上一篇
sql.DB 为什么不是一条连接,池参数应按什么容量估算
在事务函数中保证提交失败也能返回准确错误
下一篇
在事务函数中保证提交失败也能返回准确错误
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    375次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    445次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    454次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    399次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    226次使用