引入
一个单实例Redis服务器最多能支持 2^32 个key,差不多就是 2.5个亿 。那么是不是说,我们可以无限制的向缓存中加入key呢?
显然不能。容量的瓶颈不在于Redis本身,而在于内存。这正是我们需要给key设置过期时间并清除的原因。
定时删除
定时删除就是在设置key的过期时间的同时,为该key创建一个定时器(timer)。定时器会察觉到key过期,并触发立即删除动作。
除了算法简单,这种删除策略只有一个好处——保证过期key快速被删除,立即释放内存。
但是,这种删除策略对CPU极其不友好。首先,计时器是一直在跑的,在Redis服务器中创建大量的计时器是不现实的;其次,对于当前正在处理的任务,删除key的动作无疑是一种浪费。Redis服务器应该优先处理当前客户发来的请求,而不是一心二用,老想着删除过期key这码事。
惰性删除
惰性删除是一种消极的删除策略,平时放任key不管,只在每次获取key时,检查该key是否过期。
与定时删除不同,该策略对CPU非常友好。体会一下,该策略只会在非做不可的情况下才会删除过期key,而不会在平时花费任何CPU时间去处理过期key的问题。
但是,惰性删除对内存极不友好。这很显然,当某个key过期后,它仍旧保留在内存中;更坏的情况下,该key永远不会再被访问,则该key永远不会被删除。这会导致大量无用数据占用内存。
定期删除
定期删除就是每隔一段时间,对Redis数据进行一次检查,并删除检测到的过期key。
定期删除是定时删除和惰性删除的折中处理方案,它又想兼顾CPU,又想兼顾内存。
该策略的重要难点在于,如何确定删除动作的时长和频率:
1)如果删除动作的时长过长或频率过高,定期删除策略会退化为定时删除策略,浪费CPU时间在与当前任务无关的删除过期key上面;
2)如果删除动作的时长过短或频率过低,定期删除策略就会和惰性删除策略一样,留有大量无用key占用内存造成浪费。
Redis官方文档提供了一种朴素的定期删除思路:
1)每隔10秒随机抽20个key进行检测;
2)删除其中的所有过期key;
3)如果有多余25%的key过期,则重复步骤(1)
补充:集群的时钟不同步导致的key过期混乱问题
集群中的所有Redis实例的时钟必须一致,否则会导致下面这种有趣可怕的现象:
在一台实例上,我们设置所有key的过期时间为1000s,然后我们把另一台实例的计算机时间设置为未来2000s。当我们把RDB文件同步过去的时候,会发现所有的key都立即失效了!!!
如何解决呢?利用 ntp(网络时间协议) 的ntpdate命令可轻易实现集群时钟的同步。
补充:主从复制中的过期key删除问题
首先,slave不能让任何key过期,只能等待master让key过期。具体的实现是,当master中的一个key过期,便会自动合成一个针对该key的del命令,并将该命令发送给slave。
但是,del命令可能没有及时到来,slave中就会出现逻辑上(实际上)已经过期但没来得及删除的key。这时,slave会使用独自的逻辑时钟来声明该key不存在。
在lua脚本执行期间master阻塞的,所以不会触发任何key过期事件。换句话说,lua脚本执行时,master概念上的时间是停滞的、冻结的。这可以防止key在脚本中间过期,保证将相同的脚本发送到slave,从而在二者的数据集中产生相同的效果。
补充:设置key过期时间的相关命令
expire key ttl | 设置key的生存时间为ttl秒 |
pexpire key ttl | 设置key的生存时间为ttl毫秒 |
expireat key timestamp | 设置key在timestamp秒时刻过期 |
pexpireat key timestamp | 设置key在timestamp毫秒时刻过期 |
persist key | 移除key的过期时间 |
☘️ 玩转Redis专题!