【大白话说Java面试题 第175题】【07_Redis篇】第11题:Redis 的过期策略和内存淘汰策略

📅 2026/7/16 5:30:46
【大白话说Java面试题 第175题】【07_Redis篇】第11题:Redis 的过期策略和内存淘汰策略
PDF大白话说Java面试题 — 07_Redis篇第11题Redis 的过期策略和内存淘汰策略回答核心考点Redis 的过期策略和内存淘汰策略是 Redis 内存管理的核心机制大厂面试不会只问有哪几种而是深入考察惰性过期与定期过期的协同工作原理Redis 源码级、定期删除的随机采样算法activeExpireCycle、LRU 的近似实现lruclock与 24 位时钟、LFU 的计数器衰减机制logc与lfu_decay_time以及生产环境中如何根据业务特征选型。面试官真正想判断的是你是否理解 Redis 在内存和CPU之间的工程化权衡以及能否针对具体业务场景给出合理的配置建议。1. Redis 过期策略的本质Redis 中设置了过期时间的 Key其过期信息存储在过期字典expires字典中Key 指向过期时间戳。Redis 不会立即删除所有过期 Key而是采用惰性删除 定期删除的组合策略在内存占用和 CPU 开销之间取得平衡。2. 三种过期策略深度解析2.1 定时过期Time-Based Expiration原理为每个设置了过期时间的 Key 创建一个定时器到达过期时间时立即删除。优点内存友好过期 Key 被立即释放无内存残留。缺点CPU 开销巨大大量 Key 同时过期时需要创建大量定时器触发大量删除操作影响主线程Redis 是单线程模型定时删除会阻塞其他命令执行。结论Redis未采用纯定时过期策略仅在特定场景如过期 Key 被访问时触发惰性删除。2.2 惰性过期Lazy Expiration / 被动删除原理访问 Key 时先检查是否过期。如果过期则删除并返回空如果未过期则正常返回。// Redis 源码逻辑简化robj*lookupKeyRead(redisDb*db,robj*key){robj*vallookupKey(db,key);if(valexpireIfNeeded(db,key)1){// Key 已过期被删除返回 NULLreturnNULL;}returnval;}优点CPU 友好无需额外的定时扫描任务删除操作只在访问时触发实现简单无需维护定时器或扫描线程。缺点内存不友好长期未被访问的过期 Key 会一直占用内存内存泄漏风险批量过期问题如果大量 Key 同时过期且长期不被访问内存不会被释放。适用场景作为辅助策略与定期删除配合使用。2.3 定期过期Active Expiration / 主动删除原理Redis 每 100msserver.hz控制默认 10执行一次过期 Key 的随机采样删除。源码级实现activeExpireCycle// Redis 5.0 源码简化voidactiveExpireCycle(inttype){// 每次随机从 expires 字典中采样 20 个 Key// 删除其中过期的 Key// 如果过期 Key 比例 25%继续采样否则结束for(j0;jdbs_per_call;j){redisDb*dbserver.db(current_db%server.dbnum);current_db;// 采样 20 个 Keydo{dedictGetRandomKey(db-expires);if(deNULL)break;ttlgetExpire(db,dictGetKey(de));if(activeExpireCycleTryExpire(db,de,now))expired;if((iteration0xf)0){// 每 16 次迭代检查一次时间防止阻塞过久elapsedustime()-start;if(elapsedtimelimit)timelimit_exit1;}}while(expiredACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4!timelimit_exit);}}关键参数参数默认值说明server.hz10每秒执行serverCron的次数即每 100ms 一次ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP20每次采样 Key 的数量过期比例阈值25%如果过期 Key 比例 25%继续采样否则结束时间上限25ms每次activeExpireCycle的最大执行时间执行流程每 100ms 触发一次 serverCron │ ▼ ┌─────────────────────────────┐ │ activeExpireCycle │ │ 1. 随机采样 20 个 Key │ │ 2. 删除过期 Key │ │ 3. 检查过期比例 25% │ │ ├─ 是 → 继续采样 │ │ └─ 否 → 结束 │ │ 4. 检查执行时间 25ms │ │ ├─ 是 → 强制结束 │ │ └─ 否 → 继续 │ └─────────────────────────────┘优点平衡 CPU 和内存通过随机采样和比例控制避免一次性删除大量 Key 导致阻塞自适应过期 Key 多时多删少时少删动态调整。缺点清理不及时如果过期 Key 数量远大于采样数量部分过期 Key 可能长期残留内存峰值大量 Key 同时过期时定期删除无法及时清理内存可能短暂飙升。适用场景作为主策略与惰性删除配合构成 Redis 的过期策略体系。2.4 三种策略对比策略触发时机CPU 开销内存友好度阻塞风险Redis 采用定时过期定时器触发极高⭐⭐⭐⭐⭐高单线程❌ 未采用惰性过期访问 Key 时低⭐⭐无✅ 辅助定期过期每 100ms 采样中⭐⭐⭐⭐低时间上限保护✅ 主策略2.5 Redis 的实际过期策略Redis 采用“定期过期为主 惰性过期为辅”的组合策略定期过期每 100ms 主动采样删除控制内存增长惰性过期访问时检查并删除确保过期 Key 不会返回给客户端。重要认知Redis 的过期删除是概率性的不是实时的。即使 Key 已过期如果未被访问且未被定期采样到仍可能短暂存在。3. 内存淘汰策略深度解析当 Redis 内存达到maxmemory上限时需要淘汰部分 Key 以释放内存。Redis 提供 8 种淘汰策略Redis 4.0。3.1 策略分类策略范围淘汰逻辑适用场景noeviction全部 Key不淘汰直接返回错误禁止淘汰不推荐生产allkeys-lru全部 KeyLRU 淘汰最近最少使用通用首选allkeys-random全部 Key随机淘汰数据无热点allkeys-lfu全部 KeyLFU 淘汰最少使用频率热点数据差异大volatile-lru有过期时间的 KeyLRU 淘汰需保留永久 Keyvolatile-random有过期时间的 Key随机淘汰数据无热点volatile-ttl有过期时间的 Key淘汰 TTL 最短的需优先释放即将过期数据volatile-lfu有过期时间的 KeyLFU 淘汰热点数据差异大3.2 LRU 近似实现传统 LRU 的问题需要维护一个双向链表每次访问时移动节点时间复杂度 O(1) 但内存开销大且 Redis 单线程下频繁移动节点影响性能。Redis 的近似 LRU每个对象robj的lru字段24 位记录最后一次访问时间Redis 全局时钟的低 24 位精度约 1 分钟淘汰时随机采样 N 个 Key默认 5 个maxmemory-samples配置选择lru值最小最久未访问的 Key 淘汰。// Redis 对象结构简化structredisObject{unsignedtype:4;// 数据类型unsignedencoding:4;// 编码方式unsignedlru:LRU_BITS;// 24 位 LRU 时钟Redis 3.0intrefcount;// 引用计数void*ptr;// 数据指针};近似 LRU 的误差采样数量少默认 5 个可能错过真正的最久未访问 Key增大maxmemory-samples如 10可提高精度但增加 CPU 开销。maxmemory-samples近似度CPU 开销建议3低低测试环境5默认中中大多数场景10高高内存敏感场景3.3 LFU 实现Redis 4.0核心思想不仅考虑多久没访问还考虑访问频率。适合访问模式变化大的场景如某些 Key 曾经很热门现在冷门。数据结构Redis 4.0 后lru字段复用为 LFU 计数器lru 字段24 位拆分 ┌─────────────┬─────────────────┐ │ 高 16 位 │ 低 8 位 │ │ logc 计数器 │ ldt 最后访问时间 │ └─────────────┴─────────────────┘字段位数说明logcLogistic Counter16 位访问计数器非线性增长新访问1旧访问更少ldtLast Decrement Time8 位最后访问时间分钟精度用于衰减计算计数器增长规则每次访问时logc增加但增长量随当前值增大而减小非线性logc较小时每次访问 1logc较大时每次访问 0.1 或更少。最大值为 255。衰减机制每隔lfu-decay-time分钟默认 1 分钟logc衰减一次logc logc - (当前时间 - ldt) / lfu_decay_time长时间未访问的 Keylogc逐渐衰减最终被淘汰。LFU vs LRU 对比维度LRULFU核心指标最后访问时间访问频率适用场景访问模式稳定热点长期不变访问模式变化大热点会转移内存开销24 位时间戳16 位计数器 8 位时间计算复杂度低中需衰减计算典型业务商品详情页、用户资料新闻推荐、热搜榜单3.4 volatile-ttl 策略原理从设置了过期时间的 Key 中优先淘汰 TTL剩余存活时间最短的 Key。适用场景缓存数据有明确的业务过期时间如验证码 5 分钟、Token 30 分钟希望优先释放即将过期的数据减少内存浪费。注意如果所有 Key 都没有设置过期时间volatile-ttl不会淘汰任何 Key可能导致内存溢出。4. 生产环境配置建议4.1 过期策略配置场景建议大量 Key 同时过期分散过期时间EXPIRE key seconds random(0, 60)避免雪崩内存敏感监控expired_keys指标确保定期删除效率长冷数据设置合理的过期时间避免长期占用内存4.2 淘汰策略配置业务特征推荐策略理由通用缓存读多写少allkeys-lru保留热点数据淘汰冷数据热点数据变化频繁allkeys-lfu频率优先适应热点转移需保留永久 Keyvolatile-lru只淘汰有过期时间的 Key数据有明确生命周期volatile-ttl优先释放即将过期数据数据无热点allkeys-random简单随机避免 LRU 误差4.3 关键配置参数# redis.conf maxmemory 4gb # 最大内存上限 maxmemory-policy allkeys-lru # 淘汰策略 maxmemory-samples 10 # LRU/LFU 采样数量提高精度 hz 10 # serverCron 执行频率默认 105. 生产环境避坑指南5.1 大量 Key 同时过期的内存飙升如果 100 万个 Key 同时设置 1 小时过期1 小时后定期删除的采样速度每 100ms 采样 20 个每秒 200 个远跟不上过期速度内存会短暂飙升。解决方案分散过期时间EXPIRE key 3600 random(0, 300)监控used_memory和expired_keys设置告警。5.2noeviction策略的生产风险默认策略Redis 3.0 前是noeviction内存满后写入直接报错。生产环境必须显式设置淘汰策略。5.3 LFU 的logc饱和问题logc最大值为 255如果某个 Key 被频繁访问达到饱和后续访问不再增加计数器。Redis 通过非线性增长缓解但极端场景下仍需监控。5.4 大 Key 的淘汰风险如果某个大 Key如 100MB 的 Hash被 LRU 淘汰瞬间释放大量内存可能导致 Redis 阻塞删除大 Key 是同步操作。建议避免大 Key拆分为多个小 Key使用UNLINKRedis 4.0异步删除替代DEL。5.5 监控指标expired_keys每秒过期 Key 数量监控定期删除效率evicted_keys每秒淘汰 Key 数量监控内存压力used_memory当前内存使用量used_memory_rss操作系统视角的内存占用含内存碎片。6. 面试官追问与高分回答模板追问 1“Redis 的过期策略有哪些为什么不用定时过期”低分回答“有定时过期、惰性过期和定期过期。定时过期 CPU 开销大所以不用。”没有源码级细节高分回答Redis 的过期策略分三种定时过期为每个 Key 创建定时器到期立即删除。优点是内存友好但 Redis 是单线程模型大量定时器触发会阻塞主线程CPU 开销巨大因此 Redis未采用纯定时过期。惰性过期访问 Key 时检查是否过期过期则删除。优点是 CPU 友好但长期不访问的过期 Key 会残留内存导致’内存泄漏’。定期过期每 100msserver.hz控制执行activeExpireCycle随机采样 20 个 Key 删除过期项。如果过期比例 25% 且执行时间 25ms继续采样。优点是平衡 CPU 和内存但清理是概率性的不及时。Redis 实际采用‘定期过期为主 惰性过期为辅’的组合策略。追问 2“定期过期是怎么实现的采样数量是多少”高分回答定期过期由serverCron每 100ms 触发一次activeExpireCycle函数实现每次从过期字典中随机采样 20 个 KeyACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP删除其中已过期的 Key如果过期 Key 比例 25%继续采样否则结束本次循环如果执行时间 25ms强制结束防止阻塞主线程。这个设计的核心思想是’自适应采样’过期 Key 多时多删少时少删通过时间上限保护避免阻塞。但这也意味着如果过期 Key 数量远大于采样速度部分过期 Key 会短暂残留。追问 3“LRU 和 LFU 有什么区别Redis 是怎么实现的”高分回答LRULeast Recently Used和 LFULeast Frequently Used的核心区别在于淘汰指标LRU以’最后访问时间’为指标淘汰最久未访问的 Key。适合访问模式稳定的场景如商品详情页长期热门。LFU以’访问频率’为指标淘汰访问次数最少的 Key。适合访问模式变化大的场景如新闻推荐、热搜榜单。Redis 的实现LRU每个对象的lru字段24 位记录最后访问时间戳。淘汰时随机采样 N 个 Key默认 5 个选择lru最小的淘汰。这是近似 LRU不是精确的但性能更高。LFURedis 4.0复用lru字段高 16 位为logc访问计数器非线性增长低 8 位为ldt最后访问时间。每隔lfu-decay-time分钟logc衰减一次。淘汰时选择logc最小的 Key。生产环境建议通用场景用allkeys-lru热点变化频繁的场景用allkeys-lfu。追问 4“如果 Redis 内存满了写入会报错吗”高分回答取决于maxmemory-policy配置noeviction默认内存满后写入直接返回错误OOM command not allowed读操作正常。allkeys-lru/allkeys-lfu/allkeys-random从所有 Key 中按策略淘汰释放内存后写入成功。volatile-lru/volatile-lfu/volatile-random/volatile-ttl只从设置了过期时间的 Key 中淘汰。生产环境严禁使用noeviction必须显式配置淘汰策略。如果业务需要保留永久 Key使用volatile-xxx策略否则使用allkeys-lru或allkeys-lfu。追问 5“大量 Key 同时过期会导致什么问题怎么解决”高分回答大量 Key 同时过期会导致两个问题内存飙升定期删除的采样速度每秒 200 个跟不上过期速度过期 Key 短暂残留内存占用突增CPU 飙升activeExpireCycle检测到高过期比例后持续采样占用大量 CPU阻塞主线程。解决方案分散过期时间设置过期时间时增加随机偏移量如EXPIRE key 3600 random(0, 300)将过期分散到 5 分钟内监控告警监控expired_keys和used_memory突增时触发告警懒加载避免批量写入时统一设置过期时间改为按需设置。追问 6“Redis 的大 Key 删除会阻塞吗怎么优化”高分回答Redis 单线程模型下DEL大 Key如 100MB 的 Hash 或 List是同步操作会阻塞主线程导致其他命令延迟飙升。优化方案避免大 Key设计时将大 Key 拆分为多个小 Key如将大 List 拆分为每 1000 个元素一个 Key异步删除Redis 4.0 提供UNLINK命令将删除操作放入后台线程异步执行不阻塞主线程渐进式删除对 Hash/Set/ZSet 使用HSCAN/SSCAN/ZSCAN分批删除元素最后删除 Key内存淘汰时的大 Key 风险如果大 Key 被 LRU/LFU 淘汰同步删除会阻塞。建议配合UNLINK或避免大 Key。7. 方案选型速查表业务场景推荐淘汰策略过期时间设置注意事项通用缓存商品详情、用户资料allkeys-lru固定 TTL 随机偏移监控内存使用率热点变化频繁新闻、热搜allkeys-lfu固定 TTL 随机偏移关注logc衰减需保留永久配置数据volatile-lru业务数据设 TTL永久 Key 不设置 TTL数据生命周期明确验证码、Tokenvolatile-ttl精确 TTL避免全部同时过期内存极度敏感allkeys-lrumaxmemory-samples 10分散过期提高 LRU 精度大数据量、低访问频率allkeys-random固定 TTL简单随机避免 LRU 误差面试官想要的满分总结Redis 的过期策略和内存淘汰策略本质是在内存占用和 CPU 开销之间的工程化权衡。过期策略采用定期过期为主 惰性过期为辅每 100ms 的activeExpireCycle随机采样 20 个 Key过期比例 25% 且时间 25ms 时继续采样访问时惰性检查删除。这种设计避免了定时过期的 CPU 灾难也缓解了惰性过期的内存泄漏。内存淘汰策略中allkeys-lru是通用首选近似 LRU采样 5 个 Key热点变化频繁的场景用allkeys-lfu16 位计数器 衰减机制。生产环境严禁noeviction必须显式配置淘汰策略。生产避坑要点分散过期时间防止雪崩、避免大 Key 同步删除用UNLINK、监控expired_keys和evicted_keys、根据业务特征调整maxmemory-samples。最后记住Redis 的过期删除是概率性的不是实时的。设计缓存架构时不能假设 Key 过期后立即被删除必须考虑短暂的残留窗口。觉得对您有帮助麻烦点点关注啦您的关注是我创作的最大动力~