赞
踩
前言:Redis是我们常用为缓存的中间件,Redis之所以性能强,最主要的原因就是基于内存存储。然而单节点的Redis其内存大小不宜过大,会影响持久化或主从同步性能。因此我们会给一些key设置TTL过期时间,那当key过期的时候,Redis是如何处理的呢?并且由于没有及时删除,Redis的内存不够了,应该怎么办呢?本文我们会提到处理这两个问题的解决方案:过期Key处理和内存淘汰机制。
过期Key处理
我们在学习Redis缓存的时候,肯定使用过“expire”命令给Redis的key设置TTL(过期时间),如下图:
可以发现,当给一个key设置TTL时,在规定时间内访问能得到value值,但是超过TTL后再访问就只能得到nil了。我们从感觉来看,好像超过TTL值后,key会被立即删除,但是真的是这样吗?
这里我们需要提到两种Key过期处理方法:
1.1 惰性删除
惰性删除是指并不是在TTL到期后就立刻删除,而是在访问一个key的时候,检查该key的存活时间,如果已经过期才执行删除。
这里我们查看Redis源码的时候也可以很明确看到有这个处理方式:
从上图可见:在Redis对一个Key进行读写操作时,会先进入一个“expirefNeeded”函数来判断key是否过期。如果没过期则退出该函数,返回0;过期则删除该Key,返回1。然后在接下来的流程中对expirefNeeded函数返回值进行判断,如果是0则进行接下来的一系列操作,如果是1则说明过期,返回null。
1.2 周期删除
周期删除:顾明思议是通过一个定时任务,周期性的抽样部分过期的key,然后执行删除。从Redis源码可以看出,执行周期有两种:
①SLOW模式:Redis服务初始化函数initServer()中设置定时任务,按照server.hz的频率来执行过期key清理。SLOW模式的规则:
执行频率受server.hz影响,默认为10,即每秒执行10次,每个执行周期100ms。
执行清理耗时不超过一次执行周期的25%.默认slow模式耗时不超过25ms
逐个遍历db,逐个遍历db中的bucket,抽取20个key判断是否过期
如果没达到时间上限(25ms)并且过期key比例大于10%,再进行一次抽样,否则结束
②FAST模式:Redis的每个事件循环前会调用beforeSleep()函数,执行过期key清理。FAST模式的规则:
FAST模式规则(过期key比例小于10%不执行 ):
执行频率受beforeSleep()调用频率影响,但两次FAST模式间隔不低于2ms
执行清理耗时不超过1ms
逐个遍历db,逐个遍历db中的bucket,抽取20个key判断是否过期如果没达到时间上限(1ms)并且过期key比例大于10%,再进行一次抽样,否则结束
1.3 Redis过期Key处理模式
看完了上述两种过期key处理模式,理解的朋友肯定能看出这两种方式的优缺点了:
惰性删除:
优点:不用单独的有个方法不断运行去处理过期的Key,性能好。
缺点:如果某个数据量很大的key过期后一直没被访问,就会一直在Redis中,占用内存。
周期删除:
优点:不会存在Key过期后一直在Redis中占用内存的情况出现。
缺点:要不断扫描Redis中所有的Key,判断他们是否过期。如果此时Redis中有很多数据的话,会影响性能。
那么,Redis是采用哪种过期Key处理模式呢?显然,无论哪种都存在一定的缺点,当然也不否认有各自的优点。因此Redis采用二者相结合的模式:
惰性清理:每次查找key时判断是否过期,如果过期则删除
定期清理:定期抽样部分key,判断是否过期,如果过期则删除。
1.4 Redis存储结构
看完上文中的周期删除,我们会很好奇:Redis是如何知道某个Key有没有过期的呢?它难道把每个Key的TTL也挤下来了吗?这里我们不得不说Redis的数据存储库结构了。
Redis本身是一个典型的key-value内存存储数据库,因此所有的key、value都保存在Dict结构中。不过Redis本身有多个Dict,一个用来存储数据Key-Value,另一个用来存储每个Key的过期时间Key-TTL。这里我们通过查看Redis的源码,可以得到其数据库结构如下:
可见,Redis中有许多Dict结构,因此做周期删除的时候,只需要通过上图中指向存储
Key-TTL数据的Dict的指针“expires”即可。
Redis内存回收-淘汰策略
内存淘汰:就是当Redis内存使用达到设置的上限时,主动挑选部分key删除以释放更多内存的流程。
在Redis中支持八种内存淘汰策略:
noeviction: 不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略。
volatile-ttl: 对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰
allkeys-random:对全体key ,随机进行淘汰。
volatile-random:对设置了TTL的key ,随机进行淘汰。
allkeys-lru: 对全体key,基于LRU算法进行淘汰
volatile-lru: 对设置了TTL的key,基于LRU算法进行淘汰
allkeys-lfu: 对全体key,基于LFU算法进行淘汰
volatile-lfu: 对设置了TTL的key,基于LFI算法进行淘汰
这里提到了两个淘汰算法:
LRU(Least Recently Used),最少最近使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。
LFU(Least Frequently Used),最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。