Redis 缓存三大坑:穿透、雪崩与布隆过滤器(新手入门指南)

news2026/4/2 5:46:01
开篇为什么你必须懂这三个知识点想象你开了一家奶茶店。顾客点单时你会先看已经做好的成品区缓存有没有现成的奶茶有就直接端走没有再让后厨数据库现做。这个流程本来很顺畅直到三种意外发生有人故意点菜单上根本不存在的饮品成品区没有后厨也做不了但每次都得跑一趟后厨确认 —— 这叫穿透。成品区的奶茶同一时间全部过期倒掉了所有顾客一瞬间全涌向后厨 —— 这叫雪崩。而布隆过滤器就是你在店门口放的一个 快速筛查员一秒钟就能告诉顾客 我们店绝对没有这个饮品别进去问了。这三个概念是后端开发面试的必考题也是实际项目中真实会踩的坑。今天我们一次性讲清楚。一、Redis 穿透小偷绕开大门直接闯进家1.1 什么是缓存穿透正常的请求流程是这样的用户请求 → 先查 Redis 缓存 → 有数据就返回 → 没有就查数据库 → 查到后写入缓存 → 返回缓存穿透指的是用户请求的数据Redis 里没有数据库里也没有。每次请求都会查 Redis → 没有 → 查数据库 → 也没有 → 什么都没缓存 → 下次还是打到数据库通俗类比一个小偷发现你家大门Redis没锁因为根本没有这个数据的钥匙直接闯进你家里数据库翻找发现啥也没有。但他不死心带着一群人反复闯 —— 你家迟早被翻烂。1.2 为什么会发生穿透场景举例恶意攻击黑客故意用不存在的 ID如-1、99999999疯狂请求你的接口业务 Bug前端传了错误参数查询的数据压根不存在数据被删除数据库中的数据被删了但请求还在不断进来关键特征缓存和数据库双双落空。1.3 穿透的危害有多大对于新手来说记住一句话就够了穿透的本质 缓存形同虚设所有压力直接砸向数据库。正常情况1 万个请求 → 9900 个被缓存挡住 → 100 个到数据库轻松应对 穿透时 1 万个请求 → 全部穿过缓存 → 1 万个到数据库直接崩溃数据库一崩整个系统就瘫痪了 —— 用户看到的就是页面一直转圈、报错 500。1.4 怎么解决穿透方案一缓存空值最简单新手首选理解思路查了数据库发现没有也把 没有 这个结果缓存起来。public String getData(String key) { // 第一步查缓存 String value redis.get(key); if (value ! null) { // 缓存中有值包括空值标记直接返回 if (NULL_FLAG.equals(value)) { return null; // 之前查过确认不存在 } return value; } // 第二步缓存没有查数据库 String dbValue database.query(key); if (dbValue null) { // 数据库也没有 → 缓存一个空值标记设置较短的过期时间 redis.set(key, NULL_FLAG, 300); // 缓存 5 分钟 return null; } // 数据库有 → 正常缓存 redis.set(key, dbValue, 3600); // 缓存 1 小时 return dbValue; }优点简单直接几行代码搞定。缺点如果黑客用海量不同的假 key 攻击Redis 里会被塞满大量空值浪费内存。方案二布隆过滤器拦截更优雅后面第三章详细讲思路在缓存前面加一层 筛子快速判断这个数据是否可能存在。用户请求 → 布隆过滤器判断 → 不存在直接拒绝不查缓存也不查数据库 → 可能存在放行走正常缓存流程新手先记住布隆过滤器就是穿透问题的 终极方案。方案三参数校验最容易被忽略的基本功在业务入口处先校验请求参数的合法性public String getData(Long id) { // 基本校验ID 不可能小于等于 0 if (id null || id 0) { return 参数不合法; } // 正常流程... }很多穿透问题一个简单的参数校验就能挡住大半。三种方案对比方案实现难度适用场景局限性缓存空值⭐ 简单异常 key 种类少key 太多会浪费内存布隆过滤器⭐⭐ 中等大规模数据过滤有误判率不支持删除参数校验⭐ 简单所有场景的第一道防线只能挡住明显违规的请求 新手重点先理解 穿透 绕过缓存 这个核心逻辑不用深扣源码。能说出 缓存空值 布隆过滤器 两个方案面试就过关了。二、Redis 雪崩大坝同时决堤洪水冲垮后方2.1 什么是缓存雪崩缓存雪崩指的是大量缓存数据在同一时间失效或 Redis 服务直接宕机导致海量请求在同一时刻全部涌向数据库。正常缓存挡住 99% 的请求数据库很轻松 雪崩缓存突然集体罢工100% 的请求砸向数据库 → 数据库扛不住 → 系统崩溃通俗类比一座大坝平时挡住了上游的洪水大量请求。突然间大坝上所有的闸门同时打开缓存集中过期洪水瞬间冲向下游的村庄数据库—— 毁灭性的。2.2 雪崩的三大原因原因一缓存集中过期最常见// 错误示范所有缓存都设置了相同的过期时间 redis.set(product:1001, data1, 3600); // 1 小时后过期 redis.set(product:1002, data2, 3600); // 1 小时后过期 redis.set(product:1003, data3, 3600); // 1 小时后过期 // ... 1万个 key 都在同一秒过期一小时后1 万个缓存同时消失1 万个请求同时打到数据库。原因二Redis 服务宕机Redis 服务器直接挂了所有缓存瞬间全部不可用 —— 相当于大坝直接消失。原因三缓存击穿引发的连锁反应某个热点数据过期后大量请求同时穿透到数据库数据库被压垮连带其他正常请求也无法处理。2.3 雪崩的危害用一张图感受正常状态 雪崩状态 用户请求 ───→ Redis挡住99% 用户请求 ───→ Redis全部失效 ↓1%漏过去 ↓100%涌过去 数据库很轻松 数据库瞬间过载 ↓ ↓ 正常响应 超时/崩溃 ↓ 整个系统瘫痪 ↓ 用户看到页面502/5032.4 怎么解决雪崩方案一缓存过期时间加随机值最简单有效核心思路让缓存不要在同一时刻集中过期。// 正确做法在基础过期时间上加一个随机值 int baseExpire 3600; // 基础过期时间1小时 int randomExpire new Random().nextInt(600); // 随机 0~600 秒 redis.set(product:1001, data1, baseExpire randomExpire); // 3600~4200秒 redis.set(product:1002, data2, baseExpire randomExpire); // 3600~4200秒 redis.set(product:1003, data3, baseExpire randomExpire); // 3600~4200秒这样 1 万个 key 会在 3600~4200 秒之间分散过期不会同时冲击数据库。一句话记忆过期时间 固定时间 随机时间。方案二Redis 集群部署防止 Redis 宕机单台 Redis 挂了怎么办多台 Redis 互相备份。Redis 集群 ┌──────────────┐ │ Redis 主节点 │ │ (读写) │ └──────┬───────┘ ↙ ↘ ┌──────────┐ ┌──────────┐ │ Redis从节点│ │ Redis从节点│ │ (备份1) │ │ (备份2) │ └──────────┘ └──────────┘主节点挂了从节点自动顶上哨兵机制 / Cluster 模式数据有多份备份不怕单点故障新手只需知道生产环境不要用单机 Redis至少要有主从备份。方案三服务降级与熔断兜底方案当数据库压力过大时主动放弃一部分请求保护系统不被彻底压垮。用户请求商品详情 ↓ 缓存失效 数据库压力过大 ↓ 触发降级策略 - 返回默认数据商品信息加载中请稍后重试 - 或返回上次缓存的旧数据 - 而不是让所有请求都去冲击数据库类比暴风雪来了高速公路不是让所有车硬开而是临时封路降级等暴风雪过了再开放。方案四互斥锁重建缓存防止缓存击穿引发雪崩当某个热点 key 过期时不是所有线程都去查数据库而是只让一个线程去查其他线程等着。public String getDataWithLock(String key) { String value redis.get(key); if (value ! null) { return value; // 缓存命中直接返回 } // 缓存未命中尝试获取锁 String lockKey lock: key; if (redis.setnx(lockKey, 1, 30)) { try { // 拿到锁的线程去查数据库 value database.query(key); redis.set(key, value, 3600 new Random().nextInt(600)); return value; } finally { redis.del(lockKey); // 释放锁 } } else { // 没拿到锁的线程等一会儿再重试 Thread.sleep(100); return getDataWithLock(key); // 递归重试 } }四种方案总结方案解决的问题难度优先级过期时间加随机值缓存集中过期⭐必做Redis 集群Redis 宕机⭐⭐必做降级 / 熔断数据库过载兜底⭐⭐推荐互斥锁热点 key 重建时的并发冲击⭐⭐⭐推荐2.5 穿透 vs 雪崩一张表彻底分清对比维度穿透雪崩核心区别查的数据压根不存在数据存在但缓存集体失效请求数量可能是少量恶意请求一定是大量请求同时涌入缓存状态缓存和数据库都没有缓存曾经有但过期了或 Redis 挂了类比小偷闯空门大坝决堤核心方案布隆过滤器 缓存空值过期时间错开 集群 降级 一句话区分穿透是 查不存在的数据雪崩是 大量缓存同时消失。三、布隆过滤器门口的高效 筛子3.1 什么是布隆过滤器布隆过滤器Bloom Filter是一种空间效率极高的数据结构专门用来回答一个问题某个元素是否存在于集合中通俗类比你家门口放了一个智能门禁系统。有人来访时它能在0.001 秒内告诉你 这个人绝对不是业主 → 直接拒绝不开门 这个人可能是业主 → 放进来再做进一步验证注意那个 可能是—— 布隆过滤器可能会误判后面详细讲。3.2 三个核心特性新手必记表格特性说明类比高效判断速度极快时间复杂度 O (k)k 是哈希函数个数门禁刷卡只需 0.001 秒省空间只用几个比特位就能记录一个元素100 万用户只需约 1.2MB 内存有误判率说 不存在 一定对说 存在可能错门禁可能把某个陌生人误认为业主 最重要的一句话布隆过滤器说 没有 就是真没有说 有不一定真有。3.3 布隆过滤器的原理简单版不用背公式看懂下面这个过程就行第一步初始化一个位数组全是 0位数组假设 10 位[0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 0 1 2 3 4 5 6 7 8 9第二步添加元素时用多个哈希函数计算位置把对应位置设为 1添加 apple 哈希函数1(apple) 2 哈希函数2(apple) 5 哈希函数3(apple) 8 位数组变为[0, 0, 1, 0, 0, 1, 0, 0, 1, 0] ↑ ↑ ↑ 位置2 位置5 位置8再添加 banana 哈希函数1(banana) 1 哈希函数2(banana) 5 ← 注意和apple的位置5重叠了 哈希函数3(banana) 9 位数组变为[0, 1, 1, 0, 0, 1, 0, 0, 1, 1] ↑ ↑ ↑ ↑ ↑第三步查询元素时看对应位置是否全为 1查询 apple位置 2、5、8 → 全是 1 → 可能存在 ✅ 查询 cherry 哈希函数1(cherry) 2 哈希函数2(cherry) 3 ← 位置3是0 哈希函数3(cherry) 8 → 不全为 1 → 绝对不存在 ❌为什么会误判查询 grape 哈希函数1(grape) 1 ← 被 banana 设为 1 哈希函数2(grape) 5 ← 被 apple 和 banana 设为 1 哈希函数3(grape) 8 ← 被 apple 设为 1 → 全是 1 → 可能存在 → 但实际上 grape 从没添加过这就是 **误判**误判的本质不同元素的哈希值恰好占据了相同的位置撞车 了。3.4 误判率怎么控制不需要记公式记住两个规律就行操作效果增大位数组长度误判率降低空间更大撞车 概率更低增加哈希函数个数误判率先降后升太多也不好位数组很快被填满实际项目中常见配置误判率控制在 1%~5%之间已经足够用了。3.5 布隆过滤器 Redis解决缓存穿透这是布隆过滤器最经典的应用场景整体流程用户请求查询 ID9999 的商品 ↓ 第一关布隆过滤器判断 ↓ ID9999 绝对不存在 → 直接返回空不查缓存、不查数据库 ✅ ↓ ID9999 可能存在 → 继续走正常流程 ↓ 第二关查 Redis 缓存 ↓ 缓存命中 → 返回数据 缓存未命中 → 查数据库 → 写入缓存 → 返回数据代码示例伪代码新手看思路// 系统启动时把数据库中所有存在的商品 ID 加入布隆过滤器 PostConstruct public void initBloomFilter() { // 创建布隆过滤器预计放入100万个元素误判率1% BloomFilterLong bloomFilter BloomFilter.create( Funnels.longFunnel(), 1_000_000, // 预计元素数量 0.01 // 误判率 1% ); // 把数据库所有商品ID加入布隆过滤器 ListLong allProductIds database.getAllProductIds(); for (Long id : allProductIds) { bloomFilter.add(id); } } // 查询商品 public Product getProduct(Long productId) { // 第一关布隆过滤器拦截 if (!bloomFilter.mightContain(productId)) { // 布隆过滤器说绝对不存在 → 直接返回 log.info(布隆过滤器拦截商品ID {} 不存在, productId); return null; } // 第二关查 Redis 缓存 Product product redis.get(product: productId); if (product ! null) { return product; } // 第三关查数据库 product database.getProductById(productId); if (product ! null) { redis.set(product: productId, product, 3600); } else { // 布隆过滤器误判了说存在但实际不存在缓存空值兜底 redis.set(product: productId, NULL, 300); } return product; }效果对比没有布隆过滤器 黑客发送 100 万个不存在的 ID → 100 万次数据库查询 → 数据库崩溃 有布隆过滤器 黑客发送 100 万个不存在的 ID → 99 万被布隆过滤器拦截 → 1 万误判走到数据库 → 数据库轻松应对 ✅3.6 布隆过滤器的局限性新手也要知道局限说明解决方式不支持删除一旦添加元素无法删除删了可能影响其他元素使用 计数布隆过滤器进阶内容有误判率说 存在 不一定真存在配合缓存空值做兜底需要预先加载数据启动时要把已有数据全部导入新增数据时同步更新布隆过滤器3.7 Redis 中使用布隆过滤器实际开发方式Redis 4.0 以后支持通过RedisBloom 模块直接使用布隆过滤器# 创建布隆过滤器错误率 0.01预计容量 100 万 BF.RESERVE product_filter 0.01 1000000 # 添加元素 BF.ADD product_filter 1001 BF.ADD product_filter 1002 BF.ADD product_filter 1003 # 判断是否存在 BF.EXISTS product_filter 1001 # 返回 1可能存在 BF.EXISTS product_filter 9999 # 返回 0绝对不存在 # 批量操作 BF.MADD product_filter 2001 2002 2003 BF.MEXISTS product_filter 2001 9999新手建议先用 Google Guava 库的BloomFilter单机版上手简单熟悉后再用 Redis 的 RedisBloom分布式版。总结三大知识点串联回顾完整的知识链路缓存系统常见问题 ┌──────┴──────┐ ↓ ↓ 缓存穿透 缓存雪崩 查不存在的数据 大量缓存同时失效 ↓ ↓ 解决方案 解决方案 ┌────┴────┐ ┌────┴─────────┐ ↓ ↓ ↓ ↓ ↓ 缓存空值 布隆过滤器 过期时间 Redis集群 降级/熔断 ↑ 错开 │ 布隆过滤器也能辅助缓解雪崩 拦截无效请求减轻系统整体压力核心要点速记卡知识点一句话定义核心方案穿透请求的数据根本不存在缓存和数据库都查不到布隆过滤器 缓存空值雪崩大量缓存同时失效请求瞬间涌向数据库过期时间随机化 集群 降级布隆过滤器用极小空间快速判断数据是否存在的筛子说 没有 一定对说 有 可能错新手入门学习建议三步走策略阶段目标建议第一步懂场景知道穿透、雪崩分别是什么问题重点理解本文的类比和流程图第二步记方案能说出每种问题的 2~3 个解决思路面试时按照 问题→原因→方案 的逻辑回答第三步动手练用代码实现布隆过滤器和缓存策略先跑通 Demo再深入理解底层原理不要急于深扣底层源码。这三个概念的核心是架构思维和方案选型而不是让你手写一个布隆过滤器。先把 什么场景用什么方案 搞清楚你就已经超过 80% 的初学者了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2474391.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…