Redis 里那些过期数据怎么处理的,算法原理和实现细节探讨
- 问答
- 2026-01-16 00:05:20
- 4
Redis里的数据可以设置一个生存时间,时间一到,这些数据就应该被删除,释放内存,但Redis是单线程的,它不可能时时刻刻去检查每一个键是否过期,那样会极度消耗CPU资源,导致正常的命令无法执行,Redis采用了一种“惰性删除”和“定期删除”相结合的策略来巧妙地处理过期数据,这个机制的核心思想是:在空间和时间上取得平衡,既不能占用太多CPU,也不能让过期数据长期占用内存。
惰性删除:用到的时候才检查
惰性删除是Redis处理过期数据的第一道防线,也是最简单直接的方式,它的原理是:当客户端尝试访问一个键的时候,Redis才会顺带检查一下这个键是否已经过期。

每当执行GET、HGET、LRANGE等任何读取或写入键的命令时,Redis都会在执行命令之前,先查看该键是否设置了过期时间,如果设置了,则将其过期时间与当前系统时间进行对比,如果发现键已经过期,那么Redis会立即将这个键删除,然后才返回结果给客户端,对于读取命令,客户端会收到一个nil(也就是空值),就像这个键从来不存在一样。
(根据《Redis设计与实现》一书中的描述,惰性删除策略对CPU是友好的,因为它只在必要时才进行过期检查,这种策略对内存是不友好的,因为如果某个过期键之后再也没有被访问过,那么即使它已经过期,它也会一直残留在内存中,占着地方不干活,直到有人再次访问它为止,这可以看作是一种内存泄漏。)

定期删除:定期来一次大扫除
单靠惰性删除是不够的,因为总会有一些“僵尸键”永远不再被访问,为了解决这个问题,Redis启动了第二个机制:定期删除,你可以把它理解成Redis主动发起的一场周期性的“大扫除”。

定期删除的任务是由Redis的定时任务serverCron来执行的,这个任务每秒会运行多次(默认10次,即每100毫秒一次),但请注意,它并不是每次都会扫描整个数据库,那样开销太大了,相反,它采用了一种渐进式的策略:
- 抽样与时间控制:每次定期删除任务运行时,它会从一定数量的数据库中(默认是16个)随机抽取一定数量的键(默认是20个)进行检查。
- 删除与超时:对于抽中的每一个键,检查其是否过期,如果过期,则删除,Redis会严格限制这次扫描任务持续的时间,如果本次扫描耗时已经超过了预设的时间限制(默认是25毫秒),那么无论是否检查完了预设数量的键,本次扫描都会立刻停止,这是为了保证不会因为过期键删除而长时间阻塞主线程。
- 循环往复:这次没扫描完没关系,下一次
serverCron任务运行时,会从上次中断的数据库继续往下抽样扫描,这样通过多次、小范围的扫描,最终能够清理掉所有数据库中的过期键。
(在Redis的源码expire.c文件中,activeExpireCycle函数实现了这个定期删除的逻辑,其核心参数如ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP定义了每次循环检查的键数量,而执行时间则通过计算耗时来控制。)
补充机制:AOF和复制场景下的处理
过期键的删除不仅仅是在内存中移除那么简单,还需要考虑数据持久化(AOF日志)和主从复制的情况。
- AOF持久化:当某个键因为过期被惰性删除或者定期删除后,Redis会向AOF文件末尾追加一条
DEL命令,这样,当将来重建数据库时,执行AOF日志就不会把这个过期的键再恢复回来,在AOF重写过程中,重写后的新AOF文件不会包含已经过期的键。 - 主从复制:在Redis主从架构中,删除过期键的行为是由主节点(Master)主导的,主节点在删除一个过期键后,会显式地向所有从节点(Slave)发送一个
DEL命令,通知从节点也删除这个键,从而保证数据的一致性,如果客户端直接访问从节点,即使从节点发现某个键已经过期(因为主节点的DEL命令可能还没到),它也不会自己删除它,而是会继续返回该键的值,直到收到主节点的DEL命令为止,这样做是为了避免在主从数据出现短暂不一致时,从节点做出错误的删除决定。
Redis通过“惰性删除”和“定期删除”这两种方式协同工作,高效地管理着过期数据,惰性删除确保了只有在消耗CPU的时候才做检查,而定期删除则作为一个后台的清理工,防止内存被永远不被访问的过期键占满,再结合AOF和复制场景下的特殊处理,共同构成了一套在性能和资源消耗之间取得良好平衡的过期数据清理方案,这套方案的设计思想非常经典,体现了Redis作为高性能缓存系统的核心智慧。
本文由革姣丽于2026-01-16发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://www.haoid.cn/wenda/81465.html
