一、Redis 内存淘汰策略
先理解场景:
Redis 是基于内存的,内存不是无限的。
当 Redis 已经达到 maxmemory 限制,再写入新数据时,就需要决定:
是删除一些旧数据,还是直接报错?
这就是 内存淘汰策略。
常见淘汰策略
1. noeviction
不淘汰任何数据 。
如果内存满了,再写入就直接报错。
适合:对数据丢失很敏感的场景。
缺点:可能导致写请求失败。
2. allkeys-lru
在 所有 key 里,淘汰最近最少使用的数据。
意思是:
谁最近最少被访问,就先删谁。
这个很常见,适合缓存场景。
3. allkeys-lfu
在 所有 key 里,淘汰最近最不常使用的数据。
和 LRU 不同的是:
-
LRU 看"最近有没有被用过"
-
LFU 看"过去一段时间用得频不频繁"
更适合热点数据比较稳定的场景。
4. volatile-lru
只在 设置了过期时间的 key 中,淘汰最近最少使用的数据。
前提是你的 key 必须设置了 TTL。
5. volatile-lfu
只在 带过期时间的 key 中,淘汰最不常使用的数据。
6. volatile-ttl
只在设置了过期时间的 key 中,优先淘汰剩余生存时间最短的 key。
也就是谁快过期了先删谁。
7. random 系列
比如:
-
allkeys-random -
volatile-random
随机淘汰,不太常作为首选。
二、什么是大 Key
大 Key 你可以简单理解成:
某一个 Redis key 对应的 value 特别大,或者一个集合里元素特别多。
比如:
-
一个 String 存了几 MB 甚至几十 MB
-
一个 List 里几十万条数据
-
一个 Hash 里几百万 field
-
一个 Set / ZSet 特别大
大 Key 会带来什么问题
1. 网络阻塞
一次读取或删除大 Key,会传很多数据,网络开销大,响应慢。
2. Redis 阻塞
Redis 是单线程处理命令的。
如果一个命令操作大 Key 时间很长,会阻塞其他请求。
3. 内存不均衡
一个大 Key 可能占很多内存,导致内存分布不合理。
4. 删除代价高
直接 del 一个特别大的 key,可能导致 Redis 卡顿。
5. 主从复制压力大
大 Key 变更时,复制和持久化成本都更高。
三、如何处理大 Key
按"发现 + 拆分 + 删除优化"。
1. 先发现大 Key
可以通过这些方式:
-
redis-cli --bigkeys -
MEMORY USAGE key -
SCAN配合业务排查 -
监控慢日志、热点 key
意思就是先定位:
到底哪个 key 太大。
2. 拆分大 Key
这是最核心的做法。
比如 String 太大
不要一个 key 存整个大 JSON,
可以按业务字段拆分。
比如 List / Set / Hash 太大
可以做分片拆分:
java
user:msg:1
user:msg:2
user:msg:3
或者按时间拆:
java
order:202601
order:202602
本质就是:
不要让一个 key 承载太多数据,把它拆成多个小 key。
3. 删除大 Key 时要异步删除
不要直接粗暴 DEL。
更推荐:
UNLINK
因为它是异步删除,对主线程影响更小。
所以面试里可以说:
对于大 Key 删除,建议使用
UNLINK替代DEL,避免阻塞 Redis。
4. 优化数据结构设计
比如:
-
能分页查询就不要一次性塞全部数据
-
能落 MySQL / ES 的数据就不要全堆 Redis
-
只把热点、必要的数据放 Redis
5. 设置合理过期时间
避免无效数据长期堆积,慢慢长成大 Key。
四、面试回答版
Redis 内存淘汰策略 是指当 Redis 达到**
maxmemory** 限制后,再写入数据时如何处理。常见策略包括noeviction,也就是不淘汰直接报错;以及 LRU、LFU、TTL、random 等策略。其中又分为从所有 key 中 淘汰的allkeys-*和只从设置过期时间 key 中淘汰的volatile-*。实际缓存场景里,常用的是allkeys-lru或allkeys-lfu。
大 Key 指的是单个 key 对应的数据量特别大,比如超大的 String,或者元素特别多的 Hash、List、Set。这会带来网络阻塞、Redis 卡顿、删除慢、主从复制压力大等问题。处理大 Key 的核心思路是先通过bigkeys等方式定位 ,再通过业务拆分 把一个大 Key 拆成多个小 Key;如果要删除大 Key ,优先用UNLINK异步删除,避免阻塞主线程。