尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Redis 过期策略与 LRU 算法深度解析:定期删除 + 惰性删除与内存淘汰机制实战(doocs/advanced-java 高并发缓存篇)

发布时间:2026/9/18 21:17:51

资讯中心
01
ARTICLE

Redis 过期策略与 LRU 算法深度解析:定期删除 + 惰性删除与内存淘汰机制实战(doocs/advanced-java 高并发缓存篇)

Redis 过期策略与 LRU 算法深度解析:定期删除 + 惰性删除与内存淘汰机制实战(doocs/advanced-java 高并发缓存篇)
Redis 过期策略与 LRU 算法深度解析定期删除 惰性删除与内存淘汰机制实战doocs/advanced-java 高并发缓存篇【免费下载链接】advanced-java Core Interview Questions Answers For Experienced Java(Backend) Developers | 互联网 Java 工程师进阶知识完全扫盲涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识项目地址: https://gitcode.com/doocs/advanced-javaRedis 是互联网高并发架构中最核心的缓存组件而「数据写进去为什么没了」「过期数据为什么还占着内存」正是线上使用 Redis 最常踩的两个坑。本文基于 doocs/advanced-java 高并发知识体系中的 《Redis 的过期策略和 LRU 算法》 展开系统讲解 Redis 的定期删除 惰性删除双策略、maxmemory下的六种内存淘汰机制并给出可现场手写的 Java 版 LRU 缓存实现帮助你既能在面试中对答如流也能在真实项目中正确配置与使用 Redis 缓存。为什么 Redis 会丢数据先把缓存和存储分清楚很多同学在生产环境遇到过这样的现象数据写进 Redis过一会儿再读就没了。这通常不是 Bug而是把缓存当存储用导致的误判。Redis 的定位是缓存核心价值在于用内存换性能。与磁盘相比内存是宝贵而有限的资源一台机器可能只有几十 G 内存却可以有几个 T 的硬盘空间。Redis 主要基于内存完成高性能、高并发的读写操作内存空间天然是稀缺的。假设 Redis 只能使用 10G 内存你却写入了 20G 数据会发生什么—— 必然要干掉 10G 的数据只保留 10G那么干掉哪些、保留哪些—— 干掉不常用的数据保留常用的数据这就是内存淘汰要回答的问题另一类现象是key 明明设置了过期时间到了时间却依然占用着内存—— 这由 Redis 的过期策略来决定。关于缓存的本质高性能、高并发两大用途以及缓存使用不当引发的双写不一致、缓存雪崩/穿透/击穿等问题可以进一步阅读本仓库的《缓存的使用方式》。Redis 过期策略定期删除 惰性删除Redis 的过期策略可以概括为一句话定期删除 惰性删除两者配合使用。定期删除active expiration所谓定期删除指的是 Redis 默认每隔 100ms 就随机抽取一些设置了过期时间的 key检查其是否过期如果过期就删除。这里有两个关键点需要特别注意不是遍历所有 key。假设 Redis 里放了 10 万个设置了过期时间的 key如果每隔几百毫秒就全量检查一遍Redis 的 CPU 负载会飙升绝大部分开销都会消耗在检查过期 key这件事上基本等于让 Redis 死掉。因此 Redis 的做法是每隔 100ms 随机抽取一部分 key 来检查和删除把检查成本控制在可接受范围这是概率性删除。既然是随机抽样就必然存在漏网之鱼——某些过期 key 到了时间却没被抽查到也就没被删除。这一部分工作就要交给惰性删除来兜底。惰性删除lazy expiration惰性删除发生在读取路径上当你获取某个 key 的时候Redis 会检查一下这个 key 是否设置了过期时间、是否已经过期如果已经过期此时就会将其删除并且不会返回任何内容给你。获取 key 的时候如果此时 key 已经过期就删除不会返回任何东西。惰性删除的核心价值在于保证客户端读到的数据一定是活着的同时把删除动作的代价摊到访问路径上避免后台做大规模扫描。但它同样有短板——如果一个过期 key 既没被定期删除抽查到又一直没有被访问它就永远不会被惰性删除。过期时间从哪来在 Redis 中给 key 设置过期时间通常使用以下命令# 设置 key 在 60 秒后过期 EXPIRE key 60 # 以毫秒为单位设置过期时间 PEXPIRE key 60000 # 写入时直接带上过期时间最常用 SET key value EX 60 SETEX key 60 value两个策略配合仍不够时怎么办回到开头的场景如果定期删除漏掉了很多过期 key而你又没有及时去查询这些 key没走惰性删除大量过期 key 就会堆积在内存中最终导致Redis 内存耗尽。此时怎么办答案是走内存淘汰机制maxmemory 淘汰策略。过期策略负责及时清理内存淘汰机制负责兜底保命二者共同构成了 Redis 内存管理的完整闭环。内存淘汰机制六种 maxmemory 策略当 Redis 内存不足以容纳新写入的数据时会触发内存淘汰机制。Redis 提供了以下六种策略策略作用范围行为适用性评价noeviction全部键空间新写入操作直接报错不淘汰任何 key一般没人用直接拒绝写入过于粗暴allkeys-lru键空间全部 key移除最近最少使用的 key最常用覆盖所有 key 做 LRU 淘汰allkeys-random键空间全部 key随机移除某个 key一般没人用既然是淘汰理应淘汰最少使用的volatile-lru设置了过期时间的键空间移除最近最少使用的 key一般不太合适会漏掉大量未设置过期时间的 keyvolatile-random设置了过期时间的键空间随机移除某个 key很少使用volatile-ttl设置了过期时间的键空间更早过期时间的 key 优先移除适合希望优先清理即将过期数据的场景逐一拆解如下noeviction当内存不足以容纳新写入数据时新写入操作会报错。这在生产环境基本不会采用因为缓存场景下我们宁可淘汰旧数据也不能让写入全部失败allkeys-lru当内存不足以容纳新写入数据时在整个键空间中移除最近最少使用的 key。它不区分 key 是否设置了过期时间覆盖范围最全能够最大化利用有限内存服务热点数据是最常用的策略allkeys-random在整个键空间中随机移除某个 key。淘汰行为不可控、与业务热度无关一般没人用——既然要淘汰肯定是把最近最少使用的 key 干掉volatile-lru仅在设置了过期时间的键空间中移除最近最少使用的 key。如果你的 key 大部分都没有设置过期时间这个策略会无从下手所以一般不太合适volatile-random在设置了过期时间的键空间中随机移除某个 key淘汰行为同样不可控volatile-ttl在设置了过期时间的键空间中更早过期时间的 key 优先移除即优先淘汰快要过期的数据让内存淘汰方向与业务生命周期对齐。补充说明volatile-*系列策略只作用于设置过过期时间的 key。如果键空间中不存在任何设置了过期时间的 key那么volatile-lru、volatile-random、volatile-ttl的行为会退化为noeviction即写入报错这一点在使用时需要注意。此外Redis 4.0 之后官方还引入了 LFULeast Frequently Used最不经常使用系列策略如allkeys-lfu、volatile-lfu在 LRU 的基础上进一步考虑访问频率适合一次访问后就长期不用的冷数据居多的场景本文以经典的 LRU 体系为主线展开。如何配置与动态调整在redis.conf配置文件中通过以下两个参数控制内存淘汰行为# 限制 Redis 最大可用内存例如 10g maxmemory 10gb # 指定内存淘汰策略例如最常用的 allkeys-lru maxmemory-policy allkeys-lru生产环境也常常动态调整而不重启进程使用CONFIG命令即可# 查询当前内存上限与淘汰策略 CONFIG GET maxmemory CONFIG GET maxmemory-policy # 动态修改立即生效无需重启 CONFIG SET maxmemory 10gb CONFIG SET maxmemory-policy allkeys-lru关于内存上限的量级可参考本仓库《生产环境中的 Redis 是怎么部署的》中的实践线上单实例 Redis 内存一般不要超过 10g超出后可能带来 fork、持久化、淘汰扫描等多方面的稳定性问题高并发下缓存的使用也依赖 Redis 单线程模型的高效性详见《Redis 的线程模型》。为什么是 LRU让内存服务热点数据LRU 是Least Recently Used的缩写翻译过来就是最近最少使用。LRU 算法的核心思想是当缓存满需要腾空间时移除最近最少使用的数据把空间让给最新使用的数据。这条朴素策略之所以有效是因为互联网业务的访问分布天然符合局部性原理往往最常被读取的数据也就是被访问次数最多的数据热点数据。利用好 LRU 算法我们能够显著提升对热点数据的缓存命中率进而提高缓存服务的内存使用率。LRU 的直观逻辑可以用下面这张图来理解缓存按最近使用到最少使用排列当某个项被get()访问时它会被移动到缓存的最前面最近使用位置而最少使用的项会逐渐沉到列表尾部成为下一次淘汰的候选。在 Redis 内部LRU 淘汰正是依托于这种访问即更新位置的机制每次 key 被访问其 LRU 时钟都会更新内存不足时优先淘汰 LRU 时钟最久未被更新的 key。手写一个 LRU 算法基于 LinkedHashMap 的 Java 实现面试中手写 LRU通常考察两件事是否理解 LRU 的核心思想以及是否熟悉 JDK 现成数据结构。从零手工实现双向链表 哈希表版的 LRU 代码量太大现场手写不现实更务实的做法是利用 JDK 已有的LinkedHashMap实现一个 Java 版的 LRU 缓存。LinkedHashMap内部由哈希表 双向链表组成哈希表保证 O(1) 的存取定位双向链表维护元素的访问顺序。其构造器支持accessOrder参数——当accessOrder true时链表顺序即访问顺序每次get/put命中的元素都会被移动到链表尾部最新位置链表头部自然沉淀为最久未使用的元素这正好就是 LRU 需要的语义。public class LRUCacheK, V extends LinkedHashMapK, V { private int capacity; /** * 传递进来最多能缓存多少数据 * * param capacity 缓存大小 */ public LRUCache(int capacity) { super(capacity, 0.75f, true); this.capacity capacity; } /** * 如果map中的数据量大于设定的最大容量返回true再新加入对象时删除最老的数据 * * param eldest 最老的数据项 * return true则移除最老的数据 */ Override protected boolean removeEldestEntry(Map.EntryK, V eldest) { // 当 map中的数据量大于指定的缓存个数的时候自动移除最老的数据 return size() capacity; } }代码要点拆解super(capacity, 0.75f, true)三个参数的含义第一个参数initialCapacity初始容量这里直接用缓存容量capacity第二个参数loadFactor负载因子0.75f是HashMap的默认值即当元素个数超过容量 × 0.75 时触发扩容第三个参数accessOrder置为true是关键——开启访问顺序模式后LinkedHashMap内部的双向链表会按访问get/put顺序维护节点最近访问的节点移到链表尾部最久未访问的节点沉淀在链表头部removeEldestEntry(Map.EntryK, V eldest)钩子方法LinkedHashMap在每次put插入新元素后都会回调此方法参数eldest就是当前链表头部最老的节点。我们重写它在size() capacity时返回trueLinkedHashMap就会自动把最老的数据移除——这正是 LRU 淘汰动作的落点用法对外表现与普通Map完全一致put、get、remove均可直接使用超过容量自动淘汰最久未使用的条目。removeEldestEntry底层依赖的正是哈希表 双向链表的结构哈希表以 O(1) 完成 key 定位双向链表以 O(1) 完成节点的移动与头尾摘除两者结合保证了 LRU 缓存在读写和淘汰上都是高效操作。不依赖 LinkedHashMap 的经典实现思路如果你被要求不能直接用LinkedHashMap也需要掌握经典的哈希表 双向链表手写思路其骨架可以概括为哈希表HashMapkey → 链表节点保证 O(1) 定位双向链表头节点为最近使用尾节点为最少使用每个节点持有 key 和 valueget(key)在哈希表中找到节点将其从链表当前位置摘除并移动到头部put(key, value)若 key 已存在更新值并移动到头部若不存在插入到头部若此时容量超限则删除尾节点最久未使用并同步从哈希表移除。上述思路的每一步都对应一个确定的数据结构操作理解之后无论用哪种语言都能快速落地。面试答题框架与要点回顾围绕Redis 过期策略 LRU这道高频面试题可以按以下主线组织回答先定性Redis 是缓存不是存储内存有限必然存在淘汰与过期清理过期策略定期删除默认每隔 100ms 随机抽取部分设置过期时间的 key 检查删除 惰性删除访问 key 时发现过期立即删除两者互补前者防堆积、后者防漏删但不保证所有过期 key 及时被清兜底机制当过期 key 堆积导致内存耗尽时由maxmemory-policy指定的内存淘汰机制接管六种策略noeviction、allkeys-lru、allkeys-random、volatile-lru、volatile-random、volatile-ttl并说明allkeys-lru最常用、volatile-*只作用于设置过过期时间的 key手写 LRU现场给出基于LinkedHashMapaccessOrder true 重写removeEldestEntry的实现并补充哈希表 双向链表的原理级思路。延伸阅读本文属于 doocs/advanced-java 高并发架构「缓存」知识链中的一环建议结合以下同仓库文档串成完整知识体系为什么要用缓存缓存的高性能与高并发价值Redis 的数据类型与使用场景Redis 单线程模型与高并发原理Redis 持久化机制RDB 与 AOFRedis 集群模式与分布式寻址算法缓存雪崩、缓存穿透与缓存击穿缓存与数据库双写一致性生产环境中的 Redis 部署方案【免费下载链接】advanced-java Core Interview Questions Answers For Experienced Java(Backend) Developers | 互联网 Java 工程师进阶知识完全扫盲涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识项目地址: https://gitcode.com/doocs/advanced-java创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。