Redis键值过期(Expire)
Redis中可以为键设置过期时间,到达过期时间后的键无法再次获取到键值。Redis记录过期时间的方式是记录Unix时间辍,也就是从1970.1.1的00:00:00 UTC时间开始过的秒数(Redis2.6后为毫秒数),根据设置时的系统时间+过期时间计算,一旦系统时间到达,键就会过期。如果设置一个键1000秒过期,然后把系统时间调后2000秒,这个键就会立刻过期。因此多台机器运行Redis共享数据时,要注意保证多台机器的时间同步,不然可能出现在某一台机器上键过期而其他机器上还未过期的情况。
这样的特性也保证了该过期的键会准时过期,即使Redis服务宕机,只要系统时间还在变化,就不会影响过期时间。
过期键的删除策略
定时删除
最容易想到的删除策略相信一定是定时删除,即为每个有过期时间的键设定一个计时器,到了过期时间自动触发并执行删除操作。
优势:删除键及时,一过期就删除,对内存非常友好,完全不会浪费不必要的内存
劣势:这是个理想化的策略,事实是这个方法会较严重地影响到CPU的性能,因为CPU需要为每个有可能过期的键设置定时任务。
因为额外的CPU消耗,这个方法是不被Redis采用的。
实际上,Redis采用的是定期删除(active deletion)+ 惰性删除(passive deletion)的组合策略。
惰性删除
所谓惰性删除即完全不去管是否键已经过期需要删除,只在一个键被访问到的时候查看是否已经过期,如果过期执行删除。
优势:对CPU十分友好,没有额外开销
劣势:对内存很不友好,大部分时候已经过期的键都是不会再次被访问的,因此内存会长时间堆积很多已经过期的键,占用空间
惰性删除的缺点很明显,显然不能只依靠这种方式,因此Redis同时采用定期删除策略
定期删除
Redis每秒钟都会执行十次这些操作:
- 随机抽取20个设置了过期时间的键
- 删除其中已经过期了的键
- 如果删除的键的数量超过了抽取键的25%,则再次重复这三部操作
通过这两种策略的结合基本可以保证任何时候已过期但还未删除的键很少,并且CPU没有太多的额外开销。
RDB文件读取时的处理策略
在master节点读取RDB文件恢复数据时,会检查是否已经过期,只加载未过期的键,自动放弃所有过期的键。
slave节点读取RDB文件时会全部读取,不管过期与否,但是会与master保持同步。
内存淘汰策略
Redis作为内存数据库,很多时候需要确保其占用的内存不能太大,可以通过设置maxmemory
的值来做到,比如maxmemory 100mb
表示对Redis有100MB的内存占用限制,而maxmemory 0
在64位系统则表示没有任何限制,在32位系统默认为3GB限制。
那么如果Redis占用的内存已经达到了maxmemory
的值,而这时候又有添加键之类占用额外内存的命令被执行怎么办?
Redis针对这个问题给出了解决方案,可以通过配置maxmemory-policy
来设置:
- noeviction:不对存在的存储做任何操作,返回error因为内存已达到最大值,较少用,用于Redis存储的数据很重要,不能丢失,但可以接受不能写入新数据的情况
- allkeys-lru:删除Redis中所有键中lru(least recently used)的键,尝试给新的数据腾出地方,这是最常用的策略,多用于用Redis做缓存的情形
- allkeys-random:随机删除Redis中的键腾出空间,几乎不会用到
- volatile-lru:从设置了过期时间的键中淘汰lru的键
- volatile-random:从设置过期时间的键中随机淘汰键
- volatile-ttl:尝试淘汰设置过期时间的键中离过期时间最近的键
此外,Redis4.0后还提供了两个额外特殊的策略:
- allkeys-lfu:从所有键中使用LFU算法(Least Frequently Used)淘汰最不常用的键,这个比allkeys-lru理论上淘汰的键更不常用。
- volatile-lfu:同理,使用新的LFU算法
Redis的LRU算法
Redis的LRU算法其实并不是真正LRU算法的实现,因为一个真正LRU算法的实现意味着很大的内存开销,这是得不偿失的,对于Redis性能和内存是很重要的。
Redis的LRU算法是一个大概的LRU机制,有很大概率会命中使用很久之前访问的键来删除,但几乎不可能是真正没被访问时间最久的键,是一个性能与效果的权衡方案。
Redis的LFU算法
前面说了Redis的LRU算法并不是真正的LRU算法,LFU只是进一步的增加了命中访问频率低的键的可能性。具体的实现本文不做涉及,感兴趣可以阅读
Redis文档中的LRU-Cache。
码字不易~觉得有帮助的话麻烦点个赞吧~