Redis内存淘汰策略
内存淘汰策略(Eviction Policies),这是 Redis 作为缓存或内存数据库的核心机制,用于在内存不足时,决定如何删除一些数据以腾出空间存储新数据。
一、核心概念:为什么需要内存淘汰?
Redis 是一个基于内存的数据库,所有数据都存放在内存中。而内存是有限且昂贵的资源。当 Redis 使用的内存达到其最大限制时(由配置文件中的 maxmemory 参数指定),继续写入新数据就会触发 Redis 的内存淘汰机制。
如果没有这个机制,Redis 会返回错误 (error) OOM command not allowed when used memory > 'maxmemory'。
二、如何配置内存淘汰?
在 redis.conf 配置文件中,有两个关键参数:
-
设置内存上限:
# 指定 Redis 最大内存限制,0 表示不限制(64位系统默认),32位系统默认3GB。 # 强烈建议生产环境必须设置。 maxmemory 4gb -
设置淘汰策略:
# 指定达到内存上限时采用的淘汰策略。 maxmemory-policy volatile-lru
三、八大内存淘汰策略详解
Redis 提供了 8 种策略,可分为三类:不淘汰、仅淘汰设置了过期时间的键、淘汰所有键。
第一类:不淘汰(容易引发错误)
noeviction(默认策略)- 行为:当内存不足时,所有会引起内存增加的写命令(如
SET,LPUSH等)都会返回错误。读命令(如GET)可以正常执行。 - 适用场景:如果你把 Redis 当作一个真正的数据库,而不仅仅是缓存,数据绝对不能丢失,那么可以使用此策略。你需要通过其他方式(如扩容)来解决内存问题。
- 行为:当内存不足时,所有会引起内存增加的写命令(如
第二类:从设置了过期时间的键中淘汰 (volatile-*)
这类策略只会在那些设置了过期时间(TTL)的键中进行淘汰。
-
volatile-lru(Least Recently Used - 最近最少使用)- 行为:淘汰最近最久未使用并且设置了过期时间的键。
- 原理:基于“如果数据最近被访问过,那么将来被访问的概率也更高”的思想。它优先淘汰最“冷”的数据。
- 适用场景:最常用的策略。如果你的应用中有明显的热点数据和非热点数据之分,希望保留热点数据,此策略效果很好。
-
volatile-lfu(Least Frequently Used - 最不经常使用) (Redis 4.0+)- 行为:淘汰访问频率最低并且设置了过期时间的键。
- 原理:LFU 会统计每个键的访问频率(不仅仅是最近访问时间),并淘汰访问次数最少的数据。它比 LRU 能更好地区分“冷热”数据。
- 适用场景:当某些数据在短时间内被频繁访问,但之后再也不用了,使用
volatile-lru可能无法及时淘汰它(因为它的最近访问时间很新),而volatile-lfu能根据频率更好地识别并淘汰它。
-
volatile-ttl(Time To Live)- 行为:淘汰剩余过期时间最短的键。
- 原理:优先淘汰即将过期的数据,为即将要过期的数据做“缓刑”。
- 适用场景:如果你希望尽可能快地清理掉过期数据,让 Redis 中保留的数据都是离过期还有一段时间的数据,可以使用此策略。
-
volatile-random- 行为:从设置了过期时间的键中,随机选择一个进行淘汰。
- 适用场景:如果你不确定哪种策略好,并且数据的访问模式是均匀的(没有明显的冷热区分),可以用这个简单的策略。
第三类:从所有键中淘汰 (allkeys-*)
这类策略会在所有键(无论是否设置了过期时间)中进行淘汰。这意味着即使没有设置过期时间的“永久”数据也可能被删除。
-
allkeys-lru- 行为:从所有键中淘汰最近最久未使用的键。
- 适用场景:如果你把 Redis 既当作缓存,又当作持久存储,但其中一些数据的重要性明显低于另一些(即有冷热区分),可以使用此策略。它会保证最热的数据常驻内存。
-
allkeys-lfu(Redis 4.0+)- 行为:从所有键中淘汰访问频率最低的键。
- 适用场景:同样是混合使用场景,但希望根据访问频率而非最近访问时间来淘汰最“冷”的数据。
-
allkeys-random- 行为:从所有键中随机选择一个进行淘汰。
- 适用场景:如果你的数据访问模式是完全随机的,所有数据被访问的概率都差不多,可以使用此策略。
四、策略选择决策图与最佳实践
如何选择最适合的策略?可以参考以下决策流程:
flowchart TD
A[开始选择淘汰策略] --> B{数据能否丢失?};
B -- 绝对不能<br/>(Redis是主要数据库) --> C[选择 noeviction<br/>并确保内存充足];
B -- 可以丢失<br/>(Redis是缓存) --> D{是否所有数据都设置了过期时间?};
D -- 是 --> E{是否有明显的热点数据?};
E -- 是 --> F[选择 volatile-lru<br/>(最常用、最通用)];
E -- 否,访问均匀 --> G[选择 volatile-random];
E -- 否,需按频率淘汰 --> H[选择 volatile-lfu];
D -- 否<br/>(混合使用:缓存+持久存储) --> I{是否有明显的热点数据?};
I -- 是 --> J[选择 allkeys-lru];
I -- 否,访问均匀 --> K[选择 allkeys-random];
I -- 否,需按频率淘汰 --> L[选择 allkeys-lfu];
最佳实践与建议
- 一定要设置
maxmemory:这是触发淘汰策略的前提,防止 Redis 耗尽服务器内存导致系统崩溃。 - 理解你的数据访问模式:
- 如果你的数据访问有热点,优先选择
volatile-lru或allkeys-lru。这是最通用、效果最好的策略。 - 如果你的数据在短时间内爆发访问后又长期沉寂(例如热点新闻),
volatile-lfu或allkeys-lfu可能是更好的选择。 - 如果你的数据访问模式是完全随机的,可以使用
...-random策略。
- 如果你的数据访问有热点,优先选择
- 缓存 vs 数据库:
- 如果 Redis 只用作纯缓存,所有数据在数据库都有副本,那么优先使用
volatile-*策略,并为所有缓存键设置合理的过期时间(TTL)。 - 如果 Redis 是混合使用(部分数据是唯一的),建议使用
allkeys-lru并合理设置maxmemory,让 Redis 自己决定淘汰哪些数据。
- 如果 Redis 只用作纯缓存,所有数据在数据库都有副本,那么优先使用
- 监控与调整:
- 使用
INFO命令或监控工具查看evicted_keys指标。如果这个数值持续增长,说明淘汰很频繁,可能需要扩容或优化策略。 - LRU/LFU 算法在 Redis 中是一种近似算法(为了节省内存),并非绝对精确。你可以通过
maxmemory-samples配置来调整算法的精确度(默认是5,越大越精确但CPU消耗越多)。
- 使用
- 版本选择:如果可能,尽量使用 Redis 4.0+ 版本,它提供了更优秀的
LFU策略。
总结:对于大多数将 Redis 作为缓存的场景,volatile-lru 是一个安全且高效的选择。
浙公网安备 33010602011771号