redis的过期策略和定时器
欢迎拜访:雾里看山-CSDN博客
本篇主题:redis的过期策略和定时器
发布时间:2025.8.12
隶属专栏:redis
目录
- redis的过期策略
- 惰性删除
- 定期删除
- 如果大量的 key 在同一时间点过期, 会产生什么问题? 如何处理?
- 定时器
- 基于优先级队列的定时器
- 基于时间轮实现的定时器
redis的过期策略
一个redis中可能同时存在很多很多的key
,这些key
中很大一部分都有过期时间,此时,redis服务器是怎么知道哪些key
已经过期要被删除,哪些key
还没过期?
如果直接遍历所有的key,显然是行不通的,这样的效率太低了。
redis的整体使用的策略由两种:
- 惰性删除
- 定期删除
惰性删除
假设这个key已经到了过期的时间,但是暂时还没删它,key还存在。
紧接着,后面又一次访问,正好用到了这个key,于是这次访问就会让redis服务触发删除key的操作,同时再返回一个nil。
该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
定期删除
每隔一定的时间,会扫描一定数量的数据库的expires
字典中一定数量的key
,并清除其中已过期的key
。该策略是直接扫描和惰性删除的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
expires
字典会保存所有设置了过期时间的key
的过期时间数据,其中,key
是指向键空间中的某个键的指针,value
是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。
因为redis是单线程的程序。如果扫描过期
key
消耗的时间太多了,就可能导致正常处理的请求命令被阻塞了(产生了类似于执行keys*
这样的效果)。
如果大量的 key 在同一时间点过期, 会产生什么问题? 如何处理?
如果大量的 key 过期时间设置的过于集中,到过期的那个时间点,Redis 可能会出现短暂的卡顿现象。
为何会出现卡顿呢? Redis 针对过期 key 的删除, 采取 定期采样删除 + 惰性删除 两种方式结合。
如果过期 key 的数目超过总 key 数目的 25% 以上, 就会使 Redis 持续删除过期 key 直到最大时间删除时间 (默认是 25ms)。
之所以限制这个最大时间, 就是为了防止 Redis 被卡住太久
但是即使如此, 有些对性能要求较高的场景仍然会因为阻塞 25ms 导致性能下降严重。
解决方案: 可以在过期时间上加一个随机值,使得过期时间分散一些。
很多时候过期时间并不一定非得卡的那么精准。比如设定 2s 之后过期, 不一定非要正好的 2000ms,2001, 2002, 1999, 1998 甚至 2010 这些时间都是问题不大的。
因此让过期时间通过随机值分散, 就可以避免同一时刻过期的 key 太多, 从而降低触发 25% 这个阈值的可能性.
定时器
redis中并没有采用定时器的方式来实现过期key
删除的策略。
redis中没有使用定时器的原因:
基于定时器的实现,势必需要引入多线程,redis早起版本都是基于单线程的基调,引入多线程就打破了作者的初衷。
但是使用定时器的方案,在很多地方都可以用到,redis后续也不是没有可能引入计数器的可能。
以下是两种常用的定时器实现的方法:
基于优先级队列的定时器
正常的队列是先进先出,优先级队列则是按照指定的优先级先出。
优先级的高低是可以定义的,在redis的使用场景中,就可以使用过期时间越早,优先级越高。
现在假设有很多的key设置了过期时间,就可以把这些key都加入到优先级队列当中,指定优先级规则是过期时间早的,先出队列。
这样,队首元素就是要最早过期的key!!!
key1: 12:00
key2: 13:00
key3: 14:00
此时,定时器中只需要分配一个线程,让这个线程去检查队首元素是否过期即可。如果队首元素没有过期,后续元素一定没过期!!
这样,扫描线程,不需要遍历所有的key,只盯住队首元素即可。
我们可以根据当前时刻和队首的过期时间设置一个等待,让线程进行休眠,当等待时间到了或者有新的任务来了,再唤醒这个线程。处理完之后,重新根据队首设置等待时间即可。
基于时间轮实现的定时器
把时间划分成多个小段(划分的粒度可以看实际的需求)。
每个小段上都挂着一个链表,每个节点都代表着一个要执行的任务。
假设需要设置一个key,这个key将在3000ms之后过期,我们就可以根据他在每个格子之间移动的速度,将他挂载到相应的位置。
如果指针每100ms移动一个格子,该时间轮有十二个格子,我们就可以将他挂载到当前位置后面的第六个格子,这样在过期的时间到了以后,我们的指针刚好在对应的格子里面,就可以执行相应的操作了。
对于时间轮来说,每个格子是多少时间,一共多少个格子,都是可以灵活调配的。
⚠️ 写在最后:以上内容是我在学习以后得一些总结和概括,如有错误或者需要补充的地方欢迎各位大佬评论或者私信我交流!!!