news 2026/1/16 8:27:25

Redis篇4—(Redis深度剖析):内存淘汰策略与缓存的三大“天坑”

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis篇4—(Redis深度剖析):内存淘汰策略与缓存的三大“天坑”

在前面的文章中,我们聊了分布式锁、聊了持久化,这些都是在讲“怎么用好 Redis”。

但今天我们要聊一个更底层、更残酷的话题:资源限制与系统脆弱性

Redis 再快,它也是基于内存的。内存是昂贵的资源,不可能无限扩容。同时,Redis 作为数据库的挡箭牌,一旦它出了问题,数据库瞬间就会面临灭顶之灾。

今天这篇,我们就来把 Redis 的内存淘汰策略缓存击穿/穿透/雪崩彻底讲透。


一、 内存满了怎么办?(内存淘汰策略)

Redis 的性能虽然强悍,但它不是黑洞。在生产环境中,我们通常会在配置文件中设置maxmemory来限制 Redis 的最大内存使用量。

那么问题来了:当数据量存满了,新数据又要塞进来,Redis 该扔掉谁?

Redis 提供了 8 种策略,看起来眼花缭乱。其实作为博主,我建议你不要死记硬背,而是从“范围”“算法”两个维度去理解。

1. 维度一:你要对谁下手?(筛选范围)

Redis 并不总是对所有数据都“赶尽杀绝”,它把数据分为了两类圈子:

  • 圈子一:Volatile(临时工区 - 仅针对设置了 TTL 的 Key)

    • 含义:Redis 只会从设置了过期时间的 Key 中进行淘汰。

    • 场景:这非常适合“混合存储”场景。比如你的 Redis 里既存了必须持久化的重要配置(没设 TTL),又存了大量的临时缓存(设了 TTL)。当内存不足时,Redis 会很懂事地只清理那些本来就会过期的缓存,绝对不会碰你的重要数据。

  • 圈子二:Allkeys(全员区 - 针对所有 Key)

    • 含义:不管你设没设过期时间,一视同仁,大家都在被淘汰的范围内。

    • 场景:这是“纯缓存”模式的最佳选择。因为作为缓存,只要内存满了,没用的数据就该腾位置,不需要保留任何特权阶级。

2. 维度二:你要怎么挑?(筛选算法)

  • LRU (Least Recently Used)淘汰最久没被使用过的

    • 逻辑:你虽然进来的早,但最近 3 个月都没人查过你,留着浪费资源,请你走人。

  • LFU (Least Frequently Used)淘汰使用频率最低的

    • 逻辑:虽然你刚才被访问了一次,但你是这一年来唯一的访问,说明你不够热,请你走人。

  • Random随机。闭着眼瞎选一个删掉。

  • TTL剩余寿命最短。既然你马上就要过期了,不如我提前送你一程(仅适用于 Volatile 范围)。

3. 策略大盘点与选型建议

将上述两个维度组合,再加上默认策略,就构成了 8 种策略:

策略名筛选范围淘汰规则博主推荐场景
noeviction-不删除,直接报错(默认配置)适用于数据绝不能丢的场景,需配合完善的扩容监控。
volatile-lru有TTL的最久未用适用于Redis 既做缓存又做存储的混合场景。
volatile-lfu有TTL的最少频率同上。
volatile-random有TTL的随机删除极其少用。
volatile-ttl有TTL的即将过期哪怕没过期,只要快到了就删,不如 LRU 智能。
allkeys-lru所有Key最久未用最常用!适用于通用的缓存场景,保留热点数据。
allkeys-lfu所有Key最少频率适用于有些生僻数据很久才被访问一次的场景。
allkeys-random所有Key随机删除极其少用。

说明:

  • 绝大多数业务场景,直接闭眼选allkeys-lru

  • 因为缓存的本质就是“把热的数据留下”,LRU 是最符合这个直觉的算法。


二、 缓存的三大“天坑”:击穿、穿透、雪崩

Redis 就像一道堤坝,挡在脆弱的 MySQL 数据库前面。如果这道堤坝出了问题,洪水(高并发请求)就会直接冲垮数据库。

我们按照问题的**“刁钻程度”**,由点到面来分析。

1. 缓存击穿 (Cache Breakdown) —— “单点爆破”

简单来说就是热点key过期的瞬间,大量请求达到了DB。

这是最常见,但也最需要技术含量的场景。

  • 现象:

    某个 超级热点 Key(比如微博热搜、秒杀商品),并发量极高。在它 TTL 过期的一瞬间,几万个请求同时发现 Redis 没数据,同时冲向数据库去构建缓存。

    结果: 数据库瞬间被打死。

  • 解决方案:

    针对这个问题,我们有两种截然不同的思路。

    方案 A:互斥锁 (Consistency First)

    既然大家都要去查数据库,那就排队!

    • 思路:只允许一个线程去查询数据库并重建缓存,其他线程阻塞等待。

    • 流程

      1. 线程 1 发现缓存没数据。

      2. 线程 1 获取分布式锁(Mutex Lock)。

      3. 线程 1 去查库、写缓存、释放锁。

      4. 线程 2、3、4 在获取锁失败后,阻塞等待,或者休眠一会再重试(Double Check)。

    • 优点:数据一致性高。

    • 缺点:性能有损耗,大家都在等那一个人。

    方案 B:永不过期 + 逻辑过期 (Availability First)(就好像他本应该过期的,但是我留下了他用来保护数据库,反正我先返回给你,然后更新重置缓存)

    这个方案牺牲了一点点数据一致性,换取了极致的性能。这也是很多高并发系统的首选。

    • 核心思路

      • Redis 层面:Key不设置 TTL,所以物理上它永远不会过期(除非被淘汰)。

      • 业务层面:我们在 Value 的内容里,多存一个属性:expireTime(逻辑过期时间)。

    • 具体实现流程

      1. 查询缓存:请求命中缓存后,读取 Value。

      2. 判断过期:检查 Value 里的expireTime字段,看是否已过期。

      3. 未过期:直接返回数据,结束。

      4. 已过期

        • 获取锁:尝试获取互斥锁。

        • 开启异步线程:如果获取锁成功,说明我是“被选中”的那个人。我立马开启一个独立的异步线程去查询数据库、更新缓存(重置expireTime)、释放锁。

        • 返回旧值:无论是获取锁成功的当前线程,还是获取锁失败的其他线程,都不用等,直接把手里这份“已经过期”的旧数据返回给用户。

    • 说明:

      这种方案非常巧妙。它实际上是“悄悄地在后台更新”。用户在更新完成前的几百毫秒内,虽然看到的是旧数据,但系统响应速度极快,没有任何阻塞。

2. 缓存穿透 (Cache Penetration) —— “打在了空气上”

关键在⼀个透字,透过去了,即数据在 Redis 缓存和数据库中都不存在这样缓存永远 不会生效,请求都会打到数据库。(说白了,没得给你缓存的机会)

如果说击穿是“热点失效”,那穿透就是“恶意攻击”。

  • 现象:

    请求想要的数据,Redis 里没有,数据库里也没有。

    这样 Redis 就成了摆设,请求每次都直通数据库。这通常是代码 bug 或者黑客恶意攻击(一直请求 ID=-1 的数据)。

  • 解决方案

    1. 缓存空对象:数据库查不到,我也在 Redis 存一个null,并设个短一点的过期时间。下次你再来查 -1,Redis 直接告诉你“是空”,别烦数据库。

    2. 布隆过滤器 (Bloom Filter):在访问 Redis 之前加一道屏障。布隆过滤器能高效地判断“这个 ID 是否存在”。如果布隆说不存在,直接打回,连 Redis 都不用查。

  • 布隆过滤器原理分析:

如果说缓存空值是被动防御,那布隆过滤器就是主动拦截。

  • 核心原理: 底层是一个巨大的BitMap (位图)+N 个哈希函数

    • 存入时:经过 N 次 Hash 计算,将对应位置的比特位全部置为1

    • 查询时:同样的 N 次 Hash 计算,去查看对应位置。

  • 判定规则(铁律)

    • 只要有一位是 0:说明该 Key绝对不存在。 ->直接拦截,无需查库

    • 如果全都是 1:说明该 Key可能存在(存在极低概率是别的 Key 凑出来的 Hash 冲突)。 ->放行

3. 缓存雪崩 (Cache Avalanche) —— “全线崩塌”

同⼀时间⼤量key过期,导致查询全打到数据库上

最后一种情况,是规模最大的灾难。

  • 现象:

    大量的 Key 在同一时间集体过期,或者 Redis 节点直接宕机。

    这导致此刻所有的请求全部涌向数据库,数据库瞬间压力过大而崩溃。

  • 解决方案

    1. 打散过期时间:在设置 TTL 时,不要设成固定的 1 小时,而是TTL + 随机数(1-5分钟)。让 Key 排队过期,不要挤在一起。

    2. 高可用架构:利用 Redis Sentinel 或 Cluster,保证 Redis 挂了一个节点,其他的能立马顶上。

    3. 服务降级:当缓存和数据库都扛不住时,直接返回默认值或错误提示,保住系统不崩。


总结

Redis 的学习,不能只停留在命令的使用上。

  • 内存淘汰是为了让 Redis 永远只留最有价值的数据。

  • 击穿/穿透/雪崩的防御,是为了让 Redis 在面对流量洪峰和异常情况时,依然能稳稳地守护身后的数据库。

掌握了这些,你才算真正具备了驾驭大规模 Redis 集群的能力。下一篇,我们将深入 Redis 的集群架构,敬请期待!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/27 13:33:07

【医疗信息合规导出】:基于PHP的PDF与XML加密导出技术揭秘

第一章:医疗数据PHP导出格式概述在医疗信息系统开发中,数据导出功能是实现信息共享、统计分析和合规上报的关键环节。PHP作为广泛应用的服务器端脚本语言,常被用于构建医疗数据管理平台的后端服务。导出的数据格式需满足可读性、兼容性和结构…

作者头像 李华
网站建设 2025/12/15 21:08:49

你还在为Rust-PHP扩展报错崩溃?:3种高效解决方案立即上手

第一章:Rust-PHP 扩展的版本适配在构建基于 Rust 编写的 PHP 扩展时,版本兼容性是确保扩展稳定运行的关键因素。PHP 的内部 API 随版本迭代频繁变化,而 Rust 通过 php-rs 或 ext-php-rs 等绑定库与 Zend 引擎交互,因此必须精确匹配…

作者头像 李华
网站建设 2026/1/13 8:42:07

仅限高级开发者:PHP 8.6扩展开发文档未公开的7个核心结构体

第一章:PHP 8.6 扩展开发概览 PHP 8.6 作为 PHP 语言持续演进的重要版本,进一步优化了扩展开发的接口稳定性与性能表现。该版本在延续 Zend 引擎高效特性的基础上,引入了更清晰的扩展注册机制和增强的类型支持,使 C 语言编写的原生…

作者头像 李华
网站建设 2025/12/15 21:07:39

多传感器信息融合,卡尔曼滤波算法的轨迹跟踪与估计 AEKF——自适应扩展卡尔曼滤波算法

多传感器信息融合,卡尔曼滤波算法的轨迹跟踪与估计AEKF——自适应扩展卡尔曼滤波算法 AUKF——自适应无迹卡尔曼滤波算法 UKF——无迹卡尔曼滤波算法 三种不同的算法实现轨迹跟踪轨迹跟踪这活儿听起来高端,实际干起来全是坑。传感器数据像一群不听话的…

作者头像 李华
网站建设 2025/12/15 21:06:34

【NGS数据质控黄金法则】:10个R语言关键步骤确保分析可靠性

第一章:NGS数据质控的核心意义与R语言优势高通量测序(NGS)技术的迅猛发展为基因组学研究提供了前所未有的数据规模,但原始测序数据中常包含接头污染、低质量碱基和PCR重复等问题,直接影响后续分析的准确性。因此&#…

作者头像 李华
网站建设 2025/12/15 21:06:21

boost获取dll导出函数调用(C++源码)

1、概述 boost获取dll导出函数并调用,4个步骤。 1、包含头文件 2、加载dll 3、获取函数地址 4、调用函数 与windows 的GetProcessAdress方式相比,感觉boost更麻烦一点,于是用ai搜索了下区别,我觉得其中一个好处就是支持跨平台吧。 由于boost::dll::shared_library::get&…

作者头像 李华