05-过期策略、内存淘汰机制与缓存失效原理

发布时间:2026/8/18 14:09:37
05-过期策略、内存淘汰机制与缓存失效原理 过期策略、内存淘汰机制与缓存失效原理作者黒漂技术佬 | 系列Redis 缓存与高并发实战内存不像硬盘不是免费的。你的 Redis 服务器就算配了 64GB 内存也不能无限制地往里塞数据。当内存满了怎么办哪些 Key 该过期、哪些 Key 该被踢出去这就是本文要讲的——过期策略与内存淘汰机制。一、Redis 的过期策略当你执行EXPIRE key 60时Redis 怎么知道 60 秒后该删这个 key 呢它的做法是定期删除 惰性删除双管齐下。1. 定期删除Redis 每隔100ms会执行一次过期扫描从过期字典里随机抽取 20 个 Key删除其中已过期的如果过期比例超过 25%就再抽 20 个循环执行但每次总耗时不超过 25ms防止影响正常请求为什么随机抽不全量扫描过期 Key 可能几十万个全量扫描每次 100ms 那 Redis 就别干正事了。随机抽查是性能和实效之间的折中。这个机制的副作用是有些过期 Key 可能不会被立刻删除而是多活了一段时间。2. 惰性删除当客户端访问一个 Key 时Redis 先检查它是否过期。过期了就当场删掉返回空。客户端: GET expired_key Redis: 检查过期字典 → 发现已过期 → 删除 → 返回 null两种策略互相配合定期删除做大扫除惰性删除做随见随清。两者缺一不可——只有定期删除访问已过期 Key 会拿到脏数据只有惰性删除没人访问的过期 Key 永远占着内存。二、内存淘汰机制8 种策略就算有过期策略如果大量 Key 都不设置过期时间或者内存增长比删除快Redis 内存最终还是会满。这时候就要靠内存淘汰机制来腾地方。配置方式# redis.confmaxmemory 2gb# 最大内存限制 2GBmaxmemory-policy allkeys-lru# 淘汰策略达到maxmemory时Redis 会根据maxmemory-policy决定删除哪些 Key。8 种策略详解策略淘汰范围淘汰条件一句话说明noeviction不淘汰超出内存直接报错默认策略缓存场景别用allkeys-lru所有 Key淘汰最久未使用的通用推荐近似 LRUvolatile-lru设了过期时间的 Key淘汰最久未使用的LRU 仅限会过期的 Keyallkeys-lfu所有 Key淘汰使用频率最低的Redis 4.0更聪明的淘汰volatile-lfu设了过期时间的 Key淘汰使用频率最低的LFU 仅限会过期的 Keyallkeys-random所有 Key随机淘汰暴力简单volatile-random设了过期时间的 Key随机淘汰同上但只淘汰有过期时间的volatile-ttl设了过期时间的 Key淘汰剩余生存时间最短的优先踢快过期的LRU vs LFU 的区别LRULeast Recently Used谁最久没被用就淘汰谁。一天前用了一次和一小时前用了一次比一天前的被淘汰。LFULeast Frequently Used谁用的次数最少就淘汰谁。一小时用了 1000 次和一天前用了一次比后者频率低被淘汰。Redis 的 LRU 是近似 LRU不是精确记录每个 Key 的最后访问时间而是随机抽样 5 个 Key可配置淘汰其中最久未用的那个。精度足够性能友好。实际选型指南你的场景推荐策略纯缓存数据从 MySQL 加载allkeys-lru混用缓存和持久数据如计数器volatile-lru热点数据很明显二八定律allkeys-lfu数据重要性差不多allkeys-random新手大忌把 Redis 当主数据库用然后设置noeviction。内存一满所有写入直接报错OOM command not allowed服务瞬间挂掉。三、缓存与数据库不一致问题缓存数据来自数据库。数据库更新了缓存里还是旧数据——这就是经典的缓存不一致问题。为什么会出现用户下单商品库存从 100 变成 99先更新了 MySQLstock 99还没来得及更新/删除 Redis 缓存另一个请求读到 Redis 中的旧值stock 100超卖解决方案Cache Aside 模式推荐读数据 1. 先读 Redis 2. Redis 有 → 直接返回 3. Redis 没有 → 查 MySQL → 写入 Redis → 返回 写数据 1. 先更新 MySQL 2. 再删除 Redis 缓存不是更新是删除 3. 下次读取时自然从 MySQL 加载最新数据为什么是删除而不是更新缓存更新缓存有并发问题——两个写操作同时更新 MySQL 和 Redis顺序可能乱。删除缓存则简单粗暴删了下次读自然从 MySQL 拉最新值。进阶延迟双删# 写操作流程mysql.update(product,stock99)redis.delete(product:1001)time.sleep(0.5)# 等一会redis.delete(product:1001)# 再删一次第二次删除是为了清理中间可能有人读到旧数据又写回缓存的脏数据。虽然不能 100% 解决但在大多数场景下够用了。四、无人售货柜缓存过期策略设计实战需求分析一个无人售货柜系统缓存的数据大致有商品库存需要准确实时读频繁、写也频繁商品详情名称、价格、图片变化少适合长缓存设备心跳10 秒一次30 秒过期用户 Session30 分钟无操作过期限流计数1 分钟滑动窗口策略设计# 商品库存LRU 淘汰 主动更新# 库存不设固定过期时间依赖 LRU 淘汰不常用的# 每次售货成功后主动更新缓存SET cabinet:3:slot:5:stock10# 商品详情长过期1 天 手动失效SET product:1001:detail{name:可乐,price:3.5}EX86400# 设备心跳短过期30 秒SET cabinet:3:heartbeat1EX30# 用户 Session滑动过期每次访问续期SET session:token:abc123{user_id:9527}EX1800# 限流计数器固定窗口1 分钟INCR rate:user:9527:minute EXPIRE rate:user:9527:minute60maxmemory 配置# redis.confmaxmemory 2gb maxmemory-policy volatile-lru# 原因心跳/Session 等设了过期淘汰这些旧的# 库存等核心数据不设过期或手动维护不会被误删选择volatile-lru的原因只有设了过期时间的 Key 才会被淘汰。商品库存这些手动维护生命周期的数据设了过期不会丢失实际上不设过期或设很长的过期而心跳和 Session 数据过期后自然被回收。过期策略和淘汰机制是 Redis 内存管理的守门员。理解它们你才能在设计缓存系统时心中有数——知道什么时候数据会过期、什么时候会被踢、怎么防止内存爆掉。这对于无人售货柜这类需要 7×24 小时稳定运行的系统尤为重要。