当前位置: 首页 > news >正文

Redis篇(四):持久化(下)

一、Redis 过期键删除策略:三种策略的取舍与组合

Redis 中设置 key 过期时间非常简单:

# 设置 60 秒后过期EXPIRE session:user:100160# 设置具体过期时间戳EXPIREAT session:user:10011699123456# 查看剩余 TTLTTL session:user:1001

但 key 过期后,Redis 并不会立即将其从内存中删除。Redis 提供了三种过期删除策略,最终选择了惰性删除 + 定期删除的组合方案。

1.1 定时删除(Timer-Based)

核心思想:在设置 key 过期时间时,创建一个定时事件,当时间到达时,由事件处理器立即执行删除操作。

优点

  • 内存最友好:过期键被立即删除,不会占用额外内存

缺点

  • CPU 不友好:需要维护大量定时器,消耗大量 CPU 资源
  • 影响吞吐量:CPU 紧张时,删除操作会占用处理客户端请求的时间
  • 实现复杂:需要精确的时间管理系统

Redis 未采用此策略

1.2 惰性删除(Lazy Expiry)

核心思想:当客户端访问某个 key 时,Redis 才会检查该 key 是否已过期,如果过期则立即删除并返回 nil。

执行流程

客户端发起 GET/SET/EXISTS 等命令 ↓ 调用 expireIfNeeded() 检查 key 是否过期 ↓ 若已过期 → 删除 key,返回 null 给客户端 若未过期 → 不做任何处理,返回正常 value

优点

  • CPU 最友好:只在访问时检查,不消耗额外 CPU 资源
  • 实现简单:无需后台线程轮询

缺点

  • 内存不友好:如果大量过期 key 从未被访问,将长期占用内存,形成"内存泄漏"
  • 存在过期数据:在未被访问前,过期数据仍存在于内存中

Redis 采用此策略

1.3 定期删除(Periodic Expiry)

核心思想:Redis 后台线程每隔一段时间(默认 100ms),随机抽取一批 key 检查是否过期,删除其中已过期的 key。

执行流程

每 100ms 执行一次 activeExpireCycle() ↓ 从过期字典中随机抽取 20 个设置了过期时间的 key ↓ 删除其中已过期的 key ↓ 如果过期比例 > 25% ↓ 继续扫描,最多持续 25ms(避免阻塞主线程)

优点

  • 内存友好:主动清理过期 key,防止内存泄漏
  • 可控:通过限制单次扫描时间和比例,避免阻塞主线程

缺点

  • 频率难控制:扫描太频繁消耗 CPU,扫描太少导致内存泄漏
  • 存在漏网之鱼:如果过期 key 比例极高,25ms 内无法全部清理

Redis 采用此策略

1.4 Redis 的组合策略:惰性 + 定期

Redis 最终采用惰性删除 + 定期删除两种策略配合使用:

策略作用互补性
惰性删除保证访问时的实时性避免无谓的 CPU 消耗
定期删除主动清理"僵尸 key"防止内存泄漏

源码层面的实现

// 访问 key 前调用intexpireIfNeeded(redisDb*db,robj*key){// 检查 key 是否过期if(!keyIsExpired(db,key))return0;// 如果服务器在加载 RDB 文件,不删除(由加载逻辑处理)if(server.loading)return0;// 删除过期 keydeleteExpiredKeyAndPropagate(db,key);return1;}// 定期删除(每 100ms 执行)voidactiveExpireCycle(inttype){// 随机抽取 20 个过期 key// 删除已过期,若比例 > 25% 继续扫描// 最多执行 25ms}

1.5 生产环境建议

# 避免大量 key 同时过期(设置随机偏移量)EXPIRE key$((RANDOM%3600+3600))# 1~2 小时随机过期# 查看过期 key 统计INFO stats# expired_keys: 累计过期 key 数量# evicted_keys: 累计淘汰 key 数量

二、Redis 内存淘汰策略:8 种策略全解析

当 Redis 内存达到maxmemory上限时,需要淘汰部分 key 来释放空间。Redis 提供了8 种内存淘汰策略

2.1 策略分类一览

策略类型作用范围说明
noeviction不淘汰所有 key内存满时拒绝写入,返回错误
allkeys-random随机淘汰所有 key从所有 key 中随机淘汰
volatile-random随机淘汰带 TTL 的 key从设置了 TTL 的 key 中随机淘汰
volatile-ttlTTL 优先带 TTL 的 key优先淘汰即将过期的 key
volatile-lruLRU 淘汰带 TTL 的 key淘汰最久未使用的带 TTL key
allkeys-lruLRU 淘汰所有 key淘汰最久未使用的任意 key
volatile-lfuLFU 淘汰带 TTL 的 key淘汰使用频率最低的带 TTL key
allkeys-lfuLFU 淘汰所有 key淘汰使用频率最低的任意 key

2.2 配置方法

# redis.confmaxmemory 4gb maxmemory-policy allkeys-lru# 动态修改(无需重启)CONFIG SET maxmemory-policy allkeys-lfu

2.3 生产环境选型建议

场景推荐策略理由
纯缓存场景allkeys-lru最常用,保留热数据,淘汰冷数据
缓存 + 持久化混合volatile-lru保护无 TTL 的重要数据不被淘汰
有明显冷热区分allkeys-lfu比 LRU 更精准识别真正的热 key
避免使用random/ttl淘汰策略不可控,可能误淘汰热数据

2.4 LRU vs LFU 的演进

LRU(Least Recently Used)

  • 核心思想:淘汰最久未被访问的 key
  • Redis 实现:记录 24bit 的访问时间戳,采用近似 LRU(随机采样 5 个,淘汰最久未使用的)
  • 问题:如果某个 key 被大量访问后不再使用,它仍会在 LRU 链表中占据靠前位置,占用宝贵内存

LFU(Least Frequently Used)

  • 核心思想:淘汰访问频率最低的 key
  • Redis 实现:记录 16bit 访问计数 + 8bit 衰减时间,采用近似 LFU
  • 优势:更精准识别真正的"热 key",避免 LRU 的"一次性扫描"问题

LFU 的衰减机制

访问计数随时间衰减,避免"老热 key"长期占据内存 衰减周期可配置:lfu-decay-time(默认 1 分钟)

三、持久化对过期键的处理机制

Redis 的持久化机制(RDB 和 AOF)在处理过期键时,有着精细的设计,确保数据恢复后的一致性。

3.1 RDB 持久化对过期键的处理

生成 RDB 文件时

  • 过期键不会被保存到 RDB 文件中
  • 仍在有效期内的键会保留过期时间信息(绝对时间戳)
// 源码简化:RDB 生成时检查过期intrdbSaveKeyValuePair(rio*rdb,robj*key,robj*val,longlongexpiretime){// 如果 key 已过期,不写入 RDBif(expiretime!=-1&&expiretime<mstime())return0;// 写入 key-valuerdbSaveStringObject(rdb,key);rdbSaveObject(rdb,val);// 写入过期时间(绝对时间戳)if(expiretime!=-1){rdbSaveType(rdb,RDB_OPCODE_EXPIRETIME_MS);rdbSaveMillisecondTime(rdb,expiretime);}return1;}

加载 RDB 文件时

  • 主节点:加载时会检查键的过期时间,自动过滤已过期的键
  • 从节点:会完整加载所有的键(包含已过期的),依赖主节点同步 DEL 命令来删除

为什么从节点加载过期键?从节点需要保持与主节点完全一致的数据状态,如果加载时过滤过期键,可能导致主从数据不一致。从节点通过接收主节点的 DEL 命令来最终删除过期键。

3.2 AOF 持久化对过期键的处理

AOF 写入时

  • 当键过期被删除时,会追加一条 DEL 命令到 AOF 文件
  • 通过appendfsync配置控制同步策略(always/everysec/no)
# AOF 文件内容示例SET session:user:1001"active"EXPIRE session:user:100160...# 60 秒后DEL session:user:1001# 过期删除时追加

AOF 重写时

  • 重写过程会检查键的过期时间,已过期的键不会写入新的 AOF
  • 这保证了重写后的 AOF 文件更加紧凑

AOF 恢复时

  • 重放 AOF 命令时,DEL 命令正常执行
  • 由于使用的是绝对时间戳,即使服务器时间发生变更,也能正确判断过期

3.3 混合持久化对过期键的处理(Redis 4.0+)

混合持久化结合了 RDB 和 AOF 的优点:

AOF 文件结构

  • RDB 格式前缀:全量数据快照(过期键已过滤)
  • AOF 格式后缀:增量写命令(包含 DEL 过期键命令)

恢复流程

  1. 加载 RDB 前缀:快速恢复全量数据(已过期的已过滤)
  2. 重放 AOF 后缀:执行增量命令(含 DEL 过期键)
  3. 最终状态:数据一致,过期键已清理
# 开启混合持久化aof-use-rdb-preambleyes

四、主从复制中的过期键一致性

主从复制是 Redis 高可用的基石,但过期键在主从节点之间的处理存在特殊机制,理解这一点对排查数据不一致问题至关重要。

4.1 核心机制:从节点不主动删除过期键

关键设计

  • 从节点不执行过期扫描:即使 key 已过期,从节点也不会主动删除
  • 从节点依赖主节点同步 DEL 命令:主节点删除过期键后,向从节点发送 DEL 命令
  • 从节点读取过期键:在收到 DEL 命令之前,从节点可能返回逻辑上已过期但实际仍存在的数据

为什么这样设计?

  1. 保证主从数据一致性:如果从节点自行删除,可能导致主从数据状态不一致
  2. 简化从节点逻辑:从节点只需被动执行命令,无需维护复杂的过期逻辑
  3. 避免时钟不一致问题:主从节点时钟可能不同步,自行判断可能导致误判

4.2 过期键处理时序

时间点主节点从节点
T0设置 EXPIRE key 60同步接收 EXPIRE
T+60skey 到达过期时间key 到达过期时间(但不删除)
T+60s+δ惰性删除 key,生成 DEL继续保留 key
T+60s+δ+Δ向从节点发送 DEL接收 DEL,删除 key
T+60s ~ T+60s+δ+Δ过期键对客户端不可见⚠ 可能返回过期数据

4.3 生产环境注意事项

1. 从节点读取过期数据

# 从节点读取一个已过期但未被删除的 keyGET expired_key# 可能返回旧值(直到收到主节点的 DEL 命令)

解决方案

  • 从节点只读场景下,短暂的数据不一致通常可接受
  • 强一致性要求应直接读主节点
  • 业务层做好数据过期后的容错处理

2. 网络延迟的影响

如果主从之间网络延迟较大,从节点可能在较长时间内保留过期数据。监控主从复制延迟(INFO replication中的master_last_io_seconds_ago)非常重要。

3. 从节点升级为主节点

当从节点通过 Sentinel 或手动故障转移升级为主节点时:

  • 它会立即开始执行过期扫描(惰性删除 + 定期删除)
  • 之前积累的过期键会被快速清理
  • 客户端可能观察到数据"突然消失"的现象

五、综合机制对比表

机制触发条件过期键处理一致性保证
惰性删除访问 key 时过期则删除强一致(客户端视角)
定期删除每 100ms 扫描随机删除过期键最终一致
RDB 生成BGSAVE / 自动触发不写入过期键生成时一致
RDB 加载(主)启动时过滤过期键加载后一致
RDB 加载(从)启动时保留过期键依赖主节点同步 DEL
AOF 写入键过期删除时追加 DEL 命令重放后一致
AOF 重写BGREWRITEAOF不写入过期键重写后一致
主从复制主节点删除时同步 DEL 命令最终一致

六、生产环境配置最佳实践

6.1 redis.conf 核心配置

# 内存上限maxmemory 4gb# 淘汰策略(纯缓存推荐 allkeys-lru)maxmemory-policy allkeys-lru# LFU 衰减时间(分钟)lfu-decay-time1# 过期 key 采样数量(影响定期删除效率)maxmemory-samples5# 混合持久化aof-use-rdb-preambleyes# AOF 同步策略cappendfsync everysec

6.2 监控指标

# 查看内存使用情况INFO memory# 查看 key 过期统计INFO stats# expired_keys: 累计过期 key 数量# evicted_keys: 累计淘汰 key 数量# 查看主从复制状态INFO replication# master_last_io_seconds_ago: 主从通信延迟# slave_read_only: 从节点是否只读

6.3 关键监控告警

指标阈值告警级别
used_memory / maxmemory> 80%警告
evicted_keys 增长速率> 1000/min严重
expired_keys 堆积大量 key 未过期警告
缓存命中率< 90%警告
master_last_io_seconds_ago> 10s严重

七、总结

本文系统梳理了 Redis 过期键的完整生命周期管理机制:

  1. 过期删除策略:惰性删除保证实时性,定期删除防止内存泄漏,两者互补
  2. 内存淘汰策略:8 种策略中,allkeys-lruallkeys-lfu是生产环境首选
  3. 持久化处理:RDB 生成时过滤过期键,AOF 追加 DEL 命令,混合持久化兼顾两者
  4. 主从一致性:从节点不主动删除,依赖主节点同步 DEL,存在短暂不一致窗口

理解这些机制的协同工作方式,不仅能帮助你在面试中从容应对,更能在生产环境中提前规避风险,保障系统稳定性。

http://www.jsqmd.com/news/1009384/

相关文章:

  • 从投稿到接收:揭秘Elsevier官方如何最终处理你的LaTeX作者简介(附前期准备建议)
  • 别再只盯着Etsy了!这5个更适合3D模型设计师的独立站平台(附Sellfy/Sketchfab建站对比)
  • 别再纠结了!嵌入式项目选文件系统,我整理了这份YAFFS、JFFS2、UBIFS实战避坑指南
  • 为什么你需要重新认识这个AI编程助手体验优化工具?
  • 2026年质量好的贵州市政水泥管道/贵州钢筋水泥管优质供应商推荐 - 行业平台推荐
  • LDO输出电容用MLCC还是钽电容?从噪声、体积到可靠性,一次说清怎么选
  • MaxBot抢票机器人架构解析:基于Selenium的自动化票务系统技术实现
  • 第6章:子模块与子树——大仓拆小仓的陷阱
  • OneNote Markdown插件:如何用简洁语法重塑你的笔记工作流
  • 从‘盲人下山’到‘智能导航’:用生活化比喻彻底搞懂SGD、Momentum、Adagrad、Adam优化器原理
  • 2026年Q2北京演出公司品牌排行 核心维度实测对比 - 优质品牌商家
  • AI Agent 双层记忆系统:从理论到落地
  • 告别Vue2的EventBus,我在React项目里用mitt搞定了跨组件通信
  • Blender建筑生成工具终极指南:快速创建专业建筑模型
  • 2026年热门的泡沫混凝土工程/贵州泡沫混凝土施工/贵州屋面泡沫混凝土公司选择指南 - 品牌宣传支持者
  • 别再死磕协议了!从IP厂商的视角,聊聊PCIe Controller和PHY模块到底怎么选
  • 从广播到对讲机:拆解生活中FM与PM调制的真实应用场景与硬件选型
  • 从微信聊天到RabbitMQ:聊聊异步通信如何让我们的系统更“抗压”
  • 深入解析NXP Kinetis LPSCI串口驱动:从阻塞/非阻塞模式到DMA集成实战
  • 2026年靠谱的贵州水泥管/贵州排污水泥管定制加工厂家推荐 - 品牌宣传支持者
  • 别再傻傻分不清了!华为iStack与H3C IRF堆叠实战配置对比(附避坑指南)
  • 从全表扫描到覆盖索引:我是怎么干掉慢查询的
  • IEEE论文投稿前必查:作者单位排版里的5个隐形坑(LaTeX避坑指南)
  • 2026年热门的自动化配电箱/定制配电箱公司对比推荐 - 品牌宣传支持者
  • 从“分流器”到“电流检测电阻”:这个小元件的前世今生与选型实战
  • LFW数据集之外:聊聊人脸识别算法评测的那些‘坑’与真实场景挑战
  • 别再只盯着RAID了!聊聊JBOD在冷数据归档和视频制作里的那些‘实在’用法
  • 深入解析NXP Kinetis SIM模块:从HAL抽象到时钟与信号路由实战
  • 告别显存焦虑:用AWQ和GPTQ在消费级显卡上跑大模型的保姆级教程
  • PyQt5界面代码维护指南:.ui文件 vs 纯Python代码,哪种方式更适合你的项目?