数据驱逐
Redis 把数据放在内存里,内存是有上限的。写到 maxmemory 边界时,要么拒绝新写入,要么按策略逐出一些旧数据腾地方。有意思的是:为了快,Redis 并不精确地找"最该删的那个"——它靠随机采样做近似。
内存写满时会发生什么
Redis 用 maxmemory 给数据集划一条内存上限。每执行一条会占用更多内存的命令,Redis 都会检查用量;一旦超过上限,就按驱逐策略删掉一些 key,把用量压回线下。
所以实际运行时,内存用量是反复轻微越界又被拉回的锯齿状:越过上限 → 逐出 → 回到线下 → 再写入 → 再越界。策略决定的,就是每次"该逐出谁"。
一个满员的停车场。新车要进来,保安得先请走一辆。请走谁,取决于规则:最久没动的那辆(LRU)?来得最少的那辆(LFU)?还是干脆随机挑一辆?——不同的驱逐策略,就是不同的"请离场"规则。
先有上限,才有驱逐。 没设 maxmemory(或设为 0),就没有边界、也不会驱逐;在 Redis Cloud 里,这个上限就是你的数据库内存大小。
八种驱逐策略
策略由两个维度组合而成:作用范围(allkeys- 所有 key / volatile- 只限设了过期时间的 key)× 挑选规则(LRU 最久未用 / LFU 最少使用 / random 随机 / ttl 最快过期)。再加一个"什么都不删"的 noeviction。
| allkeys- · 所有 key | volatile- · 仅带 TTL 的 key | |
|---|---|---|
| LRU 最久未用 |
allkeys-lru 在全部 key 里逐出最久没被访问的。纯缓存最常用的默认。 |
volatile-lru 只在带过期时间的 key 里按 LRU 逐出。混合持久数据 + 缓存时用。 |
| LFU 最少使用 |
allkeys-lfu 在全部 key 里逐出访问频率最低的。存在明显热点时更优。 |
volatile-lfu 只在带过期时间的 key 里按 LFU 逐出。 |
| Random 随机 |
allkeys-random 随机挑一个逐出。访问大致均匀、无冷热之分时可用。 |
volatile-random 在带过期时间的 key 里随机逐出。 |
| TTL 最快过期 |
— TTL 维度只对带过期时间的 key 有意义。 |
volatile-ttl 优先逐出剩余存活时间最短的 key。适合限流、短时缓存。 |
| 不驱逐 | noeviction 不逐出任何 key。内存满后,需要额外内存的写命令直接报错,只读命令照常。若开了副本,此限制作用于主库。适合"数据一条都不能丢"的场景。 |
|
volatile-* 的陷阱:如果没有任何 key 设置了过期时间,volatile-* 会退化成 noeviction——内存满了就开始报错,而不是驱逐。用它就要确保该被缓存的 key 都带上了 TTL。
近似 LRU:不精确,但够用
真正的 LRU 需要为所有 key 维护一条按访问时间排序的链表,每次访问都要移动节点——在百万级 key 上,这个开销大到无法接受。
Redis 的做法是近似:每个对象头里存一个 24 位的 LRU 时钟(秒级精度),访问时更新。要驱逐时,随机采样 maxmemory-samples 个 key(默认 5),只在这几个样本里逐出访问时间最旧的那个。从 Redis 3.0 起,还会维护一个候选池,把历次采样中的好候选保留下来,让结果更逼近真 LRU。
CONFIG SET maxmemory-policy allkeys-lru
CONFIG SET maxmemory-samples 10 # 采样数:5→10 更接近真 LRU,代价是更多 CPU
LFU:看频率,而不是看新近
LRU 关心"多久没用过",但有些数据访问间隔长、却一直重要(比如每天跑一次的热门榜单)。LFU(Redis 4.0 起)改看访问频率:用得越频繁,越该留下。
它同样是近似的:每个对象用约 8 位的概率计数器(Morris counter)估算访问频率。两个关键旋钮——lfu-log-factor 让计数对数增长(越高涨得越慢,避免热 key 瞬间顶满);lfu-decay-time 让计数随时间衰减(曾经很热、现在冷了的 key 也能被逐出,适应访问模式的变化)。
直觉:LRU 会误删"访问间隔长但重要"的数据;LFU 用频率+衰减,把真正的热点长期留在内存里,同时给"过气热点"一个退场机制。
怎么选一个策略
没有万能策略,按数据的性质和访问模式来定。一个简单的判断顺序:
allkeys-lru 是没有特别理由时的稳妥默认——因为大多数访问符合二八分布,最近用过的确实更可能被再次用到。
配置与替代方案
在数据库级别设置
驱逐策略是每个数据库独立的设置。编辑数据库详情,修改 Data eviction policy 即可。多数数据库的默认是 volatile-lru;Active-Active 数据库默认是 noeviction。
监控驱逐是否健康
用 INFO 观察 evicted_keys 和命中率。命中率持续下滑,往往意味着策略选错、或内存该扩容了。
不想驱逐?用分层存储
如果既想装下更多数据、又不想驱逐,Redis Cloud 提供 Auto Tiering(Pro)和 Redis Flex(Essentials):把数据横跨 RAM + 闪存(SSD),热数据留在 RAM、冷数据放到闪存,用更低成本扩容而不牺牲太多性能。
关于驱逐的几个真相
用一点点精度,换巨大的速度
到了 maxmemory 边界,Redis 靠随机采样做近似 LRU / LFU,逐出样本里最该走的那个。选对范围(allkeys / volatile)和规则(LRU / LFU / random / ttl),再用 evicted_keys 和命中率验证——就能在有限内存里维持高命中。


