【从零开始——Redis 进化日志|Day3】内存满了怎么办?聊聊 Redis 的“断舍离”机制
在这里祝大家新年快乐🎆🎉,欢迎来到 Redis 进化日志 的第三天。

前两天我们把 Redis 当作一个“无限容量”的超级便签在使用,存数据存得很爽。但回到现实,不管是你那台 16G 内存的笔记本,还是公司里昂贵的服务器,内存永远是最昂贵的资源。
如果不加控制地一直往 Redis 里 SET 数据,迟早有一天内存会爆满。
很多同学在做课设时没感觉,因为数据量少。但在面试或者实习中,面试官最爱问这种极端场景:
“如果 Redis 内存满了,我再往里写数据,它是会死机?会报错?还是会偷偷删掉旧数据?”
“我设置了 1 分钟过期的 Key,1 分钟到了它真的立马从内存消失了吗?”
这两个问题,一个关乎空间(内存淘汰),一个关乎时间(过期策略)。今天这一篇,咱们就把 Redis 底层的“断舍离”机制彻底扒清楚。这是一道分水岭,跨过去,你就不是“只会调包”的小白了。
1. 内存的红线:maxmemory
首先,Redis 并不是无底洞。在 Redis 的配置文件 redis.conf 中,有一个至关重要的参数叫 maxmemory。
1.1 默认是“无限”的吗?
-
在 64 位系统下(现在的服务器基本都是):默认
maxmemory是 0,也就是不限制。 -
这就意味着,只要物理内存够,Redis 就会一直吃。如果你的服务器有 32G 内存,Redis 能吃到 30G+,直到操作系统察觉到危险,触发 OOM Killer(内存溢出杀手)把 Redis 进程强行杀掉。
1.2 生产环境怎么做?
为了不让 Redis 把整个服务器搞挂,我们通常会设置一个安全阈值。比如一台 4GB 的服务器,我们可能会分配 3GB 给 Redis。
你可以在配置文件里改,也可以直接在命令行动态设置(立即生效):
# 设置最大内存为 100MB
CONFIG SET maxmemory 100mb
那么问题来了:当数据量达到 100MB 这一刻,我要再塞进一个新的 Key,Redis 会怎么办?
这就涉及到了第一道防线:内存淘汰策略。
2. 内存淘汰:谁是那个“倒霉蛋”?
当内存用量达到 maxmemory 时,Redis 必须做出选择:是拒绝新客,还是踢走旧人?
Redis 官方提供了 8 种策略(Redis 4.0 后),但作为初学者,你只需要把最核心的 3 种逻辑刻在脑子里:
① 摆烂模式:NoEviction(默认配置)
-
逻辑:内存满了?那就不许写新数据了!
-
后果:任何尝试申请内存的写命令(如 SET、LPUSH、HSET)都会直接报错:
(error) OOM command not allowed when used memory > 'maxmemory'.
但读命令(GET)以及删除命令(DEL)依然正常工作。
-
评价:这是一种保证数据绝对安全、但不保证可用性的策略。做缓存千万别用这个,否则你的系统会因为缓存写不进去而疯狂报错。
② 优先扔掉“最没用”的:AllKeys-LRU(缓存首选)
这是最主流、最推荐的策略。
-
LRU (Least Recently Used):翻译过来叫“最近最少使用”。
-
逻辑:Redis 会“盯着”所有的 Key(AllKeys),看谁最近最长时间没被访问过。
-
Key A:1 秒前被 GET 过。
-
Key B:3 天前被 GET 过。
-
内存满了,Key B 卒。
-
-
场景:绝大多数缓存场景。因为如果一个数据很久没人查了,说明它是冷数据,删了也不心疼,把位置腾给热数据。
③ 只扔“设了过期时间”的:Volatile-LRU
-
逻辑:它也会用 LRU 算法,但它有一个原则:只动那些设置了过期时间(TTL)的 Key。至于那些没设置过期的“永久数据”,打死也不删,哪怕内存爆了报错也不删。
-
场景:如果你在 Redis 里混用了“临时缓存”和“重要数据”(比如配置参数、白名单),用这个策略可以保住重要数据不被误删。
学长笔记:
面试官有时候会追问:“LRU 有什么缺点?了解 LFU 吗?”
LRU 的 Bug:如果你扫了一次全表(全量查询),把很多冷数据读了一遍,LRU 就会误以为它们是热数据,反而把真正的热数据挤走了。
LFU (Least Frequently Used):它是按“访问频率”删的。哪怕你刚才读了一次,但如果历史访问次数很低,依然会被优先淘汰。这是 Redis 4.0 后更智能的选择。
这里附带Redis所有常见的缓存淘汰策略
目前 Redis 主要提供 8 种 淘汰策略,可以根据数据的 过期时间(TTL) 和 访问频率(LRU/LFU) 划分为三大类:
2.1. 不进行淘汰
-
noeviction(默认策略):当内存达到限制时,Redis 不会删除任何数据。它会对所有写入操作(如
SET、LPUSH)报错,但允许读取操作。适用场景: 业务不允许数据丢失,且能够通过外部监控主动扩容或清理。
2.2 对设置了过期时间的数据进行淘汰 (Volatile)
这类策略只会在设置了 EXPIRE 的 Key 中进行筛选。
-
volatile-lru:在设置了过期时间的数据中,删除 最近最少使用(Least Recently Used)的数据。
-
volatile-lfu:在设置了过期时间的数据中,删除 最不经常使用(Least Frequently Used)的数据。
-
volatile-ttl:从设置了过期时间的数据中,优先删除 剩余存活时间(TTL)最短 的数据。
-
volatile-random:从设置了过期时间的数据中,随机 删除。
2.3 对所有数据进行淘汰 (Allkeys)
这类策略不看是否设置了过期时间,而是针对全体数据进行筛选。
-
allkeys-lru:从所有数据中,删除 最近最少使用 的数据。
-
allkeys-lfu:从所有数据中,删除 最不经常使用 的数据。
-
allkeys-random:从所有数据中,随机 删除。
核心算法:LRU vs LFU
为了更直观地理解如何选择,我们需要区分这两个核心算法的区别:
| 算法 | 全称 | 核心逻辑 | 侧重点 |
| LRU | Least Recently Used | 记录最后一次访问的时间戳。 | 时间: 淘汰掉很久没被访问的数据。 |
| LFU | Least Frequently Used | 记录访问次数计数器。 | 频率: 淘汰掉访问次数最少的数据。 |
如何选择合适的策略?
-
如果你有明显的“热点数据”(即一小部分数据被频繁访问):推荐使用
allkeys-lru。 -
如果你希望根据访问频率精准控制(防止偶尔的一次扫表访问导致热点数据被误删):推荐使用
allkeys-lfu。 -
如果你的 Redis 既做缓存又做数据库(有些重要数据绝对不能丢):推荐使用
volatile-lru,并确保重要数据不设置过期时间。 -
如果数据分布非常均匀:使用
allkeys-random。
设置方法
CONFIG SET maxmemory-policy allkeys-lru
3. 过期策略:时间到了,数据真的删了吗?
搞懂了“空间不足怎么删”,再来看“时间到了怎么删”。
这是小白最容易误解的一个点。
我们在 Day 1 里学了 EXPIRE key 60。
❌ 误区:很多人以为,60 秒一到,Redis 就会有一个定时器“叮”的一声,立马把这个 Key 销毁。
✅ 真相:如果同一秒钟有 100 万个 Key 同时过期,CPU 光是处理删除操作就卡死了,哪还有空响应客户端的请求?
为了在“省内存”和“不卡顿”之间找平衡,Redis 采用的是 惰性删除 + 定期删除 的组合拳:
① 惰性删除 (Lazy Free) —— “你不来找我,我就不动”
-
原理:Key 过期了?Redis 根本不主动删它,就这样让它占着内存。
-
触发:等到下次有客户端来
GET这个 Key 时,Redis 才会检查一下:“哟,兄弟你过期了?” 然后当场把它删掉,并给客户端返回nil。 -
优点:极度节省 CPU,不需要额外的线程去监控。
-
致命缺点:如果这个 Key 过期后再也没人访问(比如一个冷门的验证码),它就会永远占着内存,造成类似内存泄露的效果。
② 定期删除 (Periodic Scan) —— “定期大扫除”
为了解决惰性删除的残留问题,Redis 会搞“随机抽查”。
-
原理:Redis 会每隔一段时间(默认 100ms),随机抽取一批设置了过期时间的 Key。
-
检查:检查这批 Key 里谁过期了,过期了就删掉。
-
循环:如果抽取的这批里过期的比例很高,它会立马再抽一批继续删,直到过期比例下降。
-
注意:是“随机抽取”,不是“全盘扫描”,否则几千万个 Key 扫一遍,CPU 还是受不了。
为了弥补惰性删除的缺点,Redis 引入了主动检查机制。
-
工作流程: Redis 默认每秒进行 10 次(可配置)内部巡检:
-
从设置了过期时间的 Key 字典中随机抽取 20 个 Key。
-
删除这 20 个 Key 中已经过期的。
-
如果过期的 Key 占比超过了 25%(即超过 5 个),则重复步骤 1,直到过期比例低于 25% 或者达到了最大执行时间限制(通常为 25ms)。
-
-
优点:通过限制删除操作的时长和频率,在 CPU 消耗和内存空间之间取得了平衡。
-
缺点:依然是概率性扫描,无法保证过期的 Key 能够立即被清理。
4. 动手查查:你的 Redis 到底用了多少内存?
光说不练假把式。现在就去命令行里敲一下这个命令,看看你的 Redis 健康状况:
> INFO memory
你会看到一大坨输出,重点关注这几个:
-
used_memory_human: 850.50K <-- 实际存数据用了多少(这是你最该关心的) -
used_memory_rss_human: 3.50M <-- 操作系统实际分配给了 Redis 多少(包含碎片) -
maxmemory_human: 0B <-- 最大内存限制(0B 代表不限制,危险!)
实战建议:
如果你的 used_memory 接近了 maxmemory,而且你的应用开始变慢,大概率就是 Redis 正在疯狂进行内存淘汰,这时候 CPU 都在忙着删数据呢。
5. 总结:Redis 的生存哲学
今天的内容比较硬核,我们总结一下**“Redis 内存管理的 3 个真相”**:
-
双重防线:
-
过期策略(惰性+定期):负责清理“时间到了”的垃圾。
-
淘汰策略(LRU/LFU):负责在“空间满了”的时候腾位置。
-
-
生产红线:
-
一定要配置
maxmemory,别让 Redis 把服务器吃挂了。 -
做缓存推荐用
allkeys-lru,让热数据留下,冷数据滚蛋。
-
-
数据并不是实时的:
-
过期的 Key 可能还在内存里(没被抽查到)。
-
Redis 并不是一个强一致性的数据库,它是在性能和准确性之间做权衡的艺术品。
-
弄懂了这些,下次面试官问你“Redis 内存满了怎么办”,你就可以自信地从配置讲到算法,再讲到 CPU 调度了。
下期预告
写了三天,我们都是在黑漆漆的命令行里敲代码。但实际开发中,谁会去命令行里存数据呢?我们都是用 Java 代码来操作的。
Java 连接 Redis 有很多种方式:
-
老牌的 Jedis
-
Spring 默认的 Lettuce
-
封装好的 RedisTemplate
它们有什么区别?怎么在 Spring Boot 里把 Redis 跑起来?
下一篇 Day 4,咱们正式离开终端,打开 IDEA,写出你的第一行 Redis Java 代码!
更多推荐




所有评论(0)