Redis(五)–内存淘汰机制(涉及到过期策略)
这个博客的内容包括以下几个点:
1.redis内存淘汰机制
2.若有大量的key需要设置同一时间过期,一般需要注意什么?
3.过期键删除策略
4.redis如何保证数据都是热点数据
一、redis内存淘汰机制
1,概念:
内存淘汰机制:redis配置文件可以设置maxmemory,内存的最大使用量,达到限度会执行内存淘汰机制(也就是,redis用作缓存时,若内存空间用满,就会自动驱逐老的数据)
2,redis中的内存淘汰机制:
- 没有配置时,默认为no-evication。
- volatile为前缀的策略:都是从已过期的数据集中进行淘汰
- allkeys为前缀的策略:都是面向所有的key进行淘汰
- LRU(least recently used):最近最少用到的
- LFU(least frequently used):最不常用的
- 他们的触发条件都是redis使用内存达到阈值时
3,淘汰策略的内部实现:
- 客户端执行一个命令,导致redis中的数据增加,占用更多的内存
- redis检查内存使用量,若超出maxmemory限制,根据策略清除部分key
- 继续执行下一条命令,以此类推
在这个过程中内存的使用量会不断地达到limit值,然后超过,然后删除部分key(使用量又降到limit以下,这样循环)
若某个命令导致大量内存占用(比如通过新key保存一个很大的set),在一段时间内,可能内存的使用量会明显超过maxmemory限制。
这里涉及到一道题:
若有大量的key需要设置同一时间过期,一般需要注意什么?
若有大量的key过期时间设置的过程中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些…
因为这个淘汰机制中会涉及到过期键的一个删除的策略,接下来就来写一写:
二、过期键删除策略:
1,过期删除策略:
redis数据库键的过期时间都保存在过期字典中,根据系统时间和存活时间判断是否过期。
redis有三种不同的删除策略:
- 1,定时删除:实现方式,创建定时器。(在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作)
- 2,惰性删除:每次获取键时,检查是否过期。(键过期了就过期了,不管。每次从dict字典中按key取值时,先检查此key是否已经过期,如果过期了就删除它,并返回nil,如果没过期,就返回键值。)
- 3,定期删除:每隔一段时间,对数据库进行一次检查,删除过期键,由算法决定删除多少过期键和检查多少数据库。
2,优缺点:
- 1,定时删除,对内存友好(通过使用定时器,定时删除策略可以保证过期键会尽可能快地被删除,并释放过期键所占用的内存),但是对cpu很不友好(因为删除操作会占用cpu的时间,如果刚好碰上了cpu很忙的时候,比如正在做交集或排序等计算的时候,就会给cpu造成额外的压力)
- 2,惰性删除,对cpu友好(程序只会在取出键时才对键进行过期检查,这可以保证删除过期键的操作只会在非做不可的情况下进行,并且删除的目标仅限于当前处理的键,这个策略不会在删除其他无关的过期键上花费任何CPU时间),对内存很不友好(如果一个键已经过期了,而这个键又仍然保留在数据库中,那么只要这个过期键不被删除,它所占用的内存就不会释放)
- 3,定期删除,是两种折中,但是,如果删除太频繁,将退化为定时删除,如果删除次数太少,将退化为惰性删除。
其实定期删除的难点是确定删除操作的时长和频率。
如果删除操作太频繁,或者执行的时间太长,定期删除策略就会退化成定时删除策略,以至于将CPU时间过多地消耗在删除过期键上面。
如果删除操作执行的太少,或者执行的时间太短,定期删除策略又会和惰性删除策略一样,出现浪费内存的情况。
因此,如果采用定期删除策略的话,服务器必须根据情况,合理地设置删除操作的执行时长和频率。
3,Redis使用的策略:
redis使用的过期键值删除策略是:惰性删除加上定期删除,两者配合使用,服务器可以很好的在合理利用CPU时间和避免浪费内存空间之间取得平衡。
三、redis如何保证数据都是热点数据
这个为什么要放到这里来写,因为这个也涉及到过期策略这个知识点。
如何保证redis中的数据都是热点数据?redis内存数据集达到一定的大小的时候,就会实现数据淘汰策略,内存中的淘汰机制的初衷就是为了 更好的使用内存。
1,先设置redis过期策略:
当设置一个key的时候,一般会给这个key设置一个过期时间(expire time),当时间到了,可以采用定期删除+惰性删除。
2,但是定期删除漏掉了很多过期key,然后又没及时去做查询,也没有走惰性删除。
此时,可能会有大量的过期的key堆积在内存中,导致内存块耗尽。这时候走内存淘汰机制。(上面写的。)
3,以上保证了删除过期+留出内存空间,但又如何确保数据是最热门的数据?
其解决方案:
(1)、热点数据排序(点击次数):既然是热门数据,那么就需要有排序,使用redis中的zset数据类型是很自然的想法,数据中的某个唯一字段作为zset中的value,而点击次数作为score,记为click_zset,这样就可以选出做热门的数据,而数据直接用hashmap存储。
(2)、热点数据时间(近期访问):既然只能存比如说1w条数据且需要是热门数据,那么点击次数是一方面,时效也是一方面,如何保证?=====>可以另起一个zset,数据的字段为value,而每次点击时更新当前时间戳为其score,记为time_zset,这样就可以记录时间。在后台跑一个任务,间隔一定时间段删除两个zset中长时间没有发生点击事件的键,并删除hash数据,为产生的新数据腾出数据空间。
(3)、处理新热点数据:若有空间,则保存到自己的hashmap,并将key存到两个zset中,若没有空间时,就应该在click_zset中取出点击次数排在最前的第1w位后面的键,删除对应的hash数据,然后看这1w个score的值,然后把key放入到zset中即可。