Redis 内存淘汰与过期策略

引言

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实现

  1. RedisObject的lru字段记录数据最近一次访问时间。

  2. 第一次淘汰数据时,根据配置(maxmemory-samples)选择出来N个数据作为候选集合,把lru字段值最小的数据淘汰。

  3. 再次淘汰数据时,能进入候选集合的数据的 lru 字段值必须小于候选集合中最小的 lru 值,当候选数据集中的数据个数达到了配置值时,把候选数据集中 lru 字段值最小的数据淘汰出去。

LFU算法实现

最不经常使用算法。先比较访问次数,再比较访问时间。相对LRU可以减少全表扫描造成的缓存污染(需要淘汰的数据无法被淘汰)。

淘汰时先根据数据 lru 字段的后 8bit 选择访问次数最少的数据进行淘汰。当访问次数相同时,再根据 lru 字段的前 16bit 值大小,选择访问时间最久远的数据进行淘汰。

Redis的LFU实现

  1. RedisObject中lru字段拆成两部分,第一部分前16bit 存储访问时间,第二部分后8bit(最大255) 存储访问次数。

  2. 当前的访问次数 * lfu_log_factor(建议设置为10)+ 1,再取倒数,将此数值与(0,1)随机数比较,如果大于随机数则访问次数加1。

  3. 衰减策略,如果lfu_decay_time为1,N分钟内没有被访问,访问次数 - N,如果lfu_decay_time为2,N分钟内没有被访问,访问次数 - N/2。

  4. 第一次淘汰数据时,根据配置(maxmemory-samples)选择出来N个数据作为候选集合,把lru字段值最小的数据淘汰。

  5. 再次淘汰数据时,能进入候选集合的数据的 lru 字段值必须小于候选集合中最小的 lru 值,当候选数据集中的数据个数达到了配置值时,把候选数据集中 lru 字段值最小的数据淘汰出去。

Redis过期策略

当Redis中的键在设定的过期时间到达后如何自动删除。

惰性删除:当一个数据的过期时间到了以后,并不会立即删除数据,而是等到再有请求来读写这个数据时,对数据进行检查,如果发现数据已经过期了,再删除这个数据。

定期删除:Redis 每隔一段时间(默认 100ms),就会随机选出一定数量的数据,检查它们是否过期,并把其中过期的数据删除,这样就可以及时释放一些内存。

主动淘汰删除:通过淘汰策略,内存达到 maxmemory 限制时根据淘汰策略进行删除。

如何避免读到过期数据?

  1. Redis 3.2 之前的版本从库在服务读请求时,并不会判断数据是否过期,而是会返回过期数据。在 3.2 版本后,Redis 做了改进,如果读取的数据已经过期了,从库虽然不会删除,但是会返回空值,这就避免了客户端读到过期数据。

  2. 使用EXPIRE(设置key存活时间为多少秒) 和 PEXPIRE(设置key存活时间为多少毫秒)命令设置过期时间时,主从库之间的同步延迟会导致主从库的过期时间不一样,通过从库读取时会读到过期数据。建议使用EXPIREAT和PEXPIREAT命令,指定过期时间。


感谢您的阅读!如果文章中有任何问题或不足之处,欢迎及时指出,您的反馈将帮助我不断改进与完善。期待与您共同探讨技术,共同进步!

相关推荐
huihuihuanhuan.xin2 小时前
spring循环依赖以及补充相关知识
java·后端·spring
繁星星繁2 小时前
Docker(一)
java·c语言·数据结构·c++·docker·容器·eureka
编程大师哥2 小时前
JAVA 动态代理
java·开发语言
圣光SG2 小时前
Java类与对象及面向对象基础核心详细笔记
java·前端·数据库
白露与泡影2 小时前
从 BIO 到 epoll:高并发 I/O 模型演进与本质分析
java·服务器·数据库
学编程就要猛2 小时前
JavaEE进阶:Spring Boot快速上手
java·spring boot·java-ee
zuowei28892 小时前
SpringBootInvalid bound statement (not found)的原因和解决方案
mybatis
shark22222222 小时前
springboot中配置logback-spring.xml
spring boot·spring·logback