引言
Redis 作为内存数据库,内存资源有限,必须妥善处理内存占用问题。本文梳理两种核心机制:淘汰策略 决定内存达到上限时如何移除数据,涵盖 noeviction、LRU、LFU 等多种算法及其实现细节;过期策略(惰性删除 + 定期删除)负责及时清理已过期的键,并说明如何避免读到过期数据。掌握这些机制,有助于合理配置 Redis 实例,平衡内存效率与数据可用性。
Redis淘汰策略
当 Redis 的内存使用量达到配置(maxmemory)的上限时,如何决定哪些数据应该被删除以释放内存。
淘汰策略是在全局范围内应用的,它适用于 Redis 实例中的所有键。

-
noeviction:一旦缓存被写满了,再有写请求来时,Redis 不再提供服务,而是直接返回错误。
-
volatile-ttl:会针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的越先被删除。
-
volatile-random:在设置了过期时间的键值对中,进行随机删除。
-
volatile-lru:会使用 LRU 算法筛选设置了过期时间的键值对(默认策略)。
-
volatile-lfu:会使用 LFU 算法选择设置了过期时间的键值对。
-
allkeys-random:从所有键值对中随机选择并删除数据。
-
allkeys-lru:使用 LRU 算法在所有数据中进行筛选。
-
allkeys-lfu:使用 LFU 算法在所有数据中进行筛选。
LRU算法实现
最近最少使用算法。比较访问时间,淘汰最近一次访问时间小的数据。
Redis的LRU实现
-
RedisObject的lru字段记录数据最近一次访问时间。
-
第一次淘汰数据时,根据配置(maxmemory-samples)选择出来N个数据作为候选集合,把lru字段值最小的数据淘汰。
-
再次淘汰数据时,能进入候选集合的数据的 lru 字段值必须小于候选集合中最小的 lru 值,当候选数据集中的数据个数达到了配置值时,把候选数据集中 lru 字段值最小的数据淘汰出去。
LFU算法实现
最不经常使用算法。先比较访问次数,再比较访问时间。相对LRU可以减少全表扫描造成的缓存污染(需要淘汰的数据无法被淘汰)。
淘汰时先根据数据 lru 字段的后 8bit 选择访问次数最少的数据进行淘汰。当访问次数相同时,再根据 lru 字段的前 16bit 值大小,选择访问时间最久远的数据进行淘汰。
Redis的LFU实现
-
RedisObject中lru字段拆成两部分,第一部分前16bit 存储访问时间,第二部分后8bit(最大255) 存储访问次数。
-
当前的访问次数 * lfu_log_factor(建议设置为10)+ 1,再取倒数,将此数值与(0,1)随机数比较,如果大于随机数则访问次数加1。
-
衰减策略,如果lfu_decay_time为1,N分钟内没有被访问,访问次数 - N,如果lfu_decay_time为2,N分钟内没有被访问,访问次数 - N/2。
-
第一次淘汰数据时,根据配置(maxmemory-samples)选择出来N个数据作为候选集合,把lru字段值最小的数据淘汰。
-
再次淘汰数据时,能进入候选集合的数据的 lru 字段值必须小于候选集合中最小的 lru 值,当候选数据集中的数据个数达到了配置值时,把候选数据集中 lru 字段值最小的数据淘汰出去。
Redis过期策略
当Redis中的键在设定的过期时间到达后如何自动删除。
惰性删除:当一个数据的过期时间到了以后,并不会立即删除数据,而是等到再有请求来读写这个数据时,对数据进行检查,如果发现数据已经过期了,再删除这个数据。
定期删除:Redis 每隔一段时间(默认 100ms),就会随机选出一定数量的数据,检查它们是否过期,并把其中过期的数据删除,这样就可以及时释放一些内存。
主动淘汰删除:通过淘汰策略,内存达到 maxmemory 限制时根据淘汰策略进行删除。
如何避免读到过期数据?
-
Redis 3.2 之前的版本从库在服务读请求时,并不会判断数据是否过期,而是会返回过期数据。在 3.2 版本后,Redis 做了改进,如果读取的数据已经过期了,从库虽然不会删除,但是会返回空值,这就避免了客户端读到过期数据。
-
使用EXPIRE(设置key存活时间为多少秒) 和 PEXPIRE(设置key存活时间为多少毫秒)命令设置过期时间时,主从库之间的同步延迟会导致主从库的过期时间不一样,通过从库读取时会读到过期数据。建议使用EXPIREAT和PEXPIREAT命令,指定过期时间。
感谢您的阅读!如果文章中有任何问题或不足之处,欢迎及时指出,您的反馈将帮助我不断改进与完善。期待与您共同探讨技术,共同进步!