1.为什么需要淘汰策略
当redis占用的内存超过服务器的为例内存的时候,就会触发系统的swap,这时内存回合磁盘频繁交换,导致redis性能急剧下降,这时对于访问频繁的redis来说,服务相当于不可用状态,为了防止出现这种情况,redis提供了maxmemory参数来控制redis占用的最大内存大小,防止出现swap.但是一旦设置了内存的最大值 ,就可能出现内存使用到达最大值的情况,这时redis的淘汰策略就排上用场了.当reids使用的内存到达设置的最大内存的时候redis的淘汰策略开始执行
2.redis的淘汰策略有哪些
1.noeviction
默认的淘汰策略,不会继续服务写请求.当有写请求过来的时候直接失败,但是可以详情del请求.其他请求也是正常的
2.allkeys-lru
对所有的键执行lru操作,即淘汰最长时间不使用的键,
3.allkeys-lfu
对所有的键执行lfu操作,即淘汰使用频次最低的键
4.allkeys-random
5.volatile-lru
和allkeys-lru一样,但是只会对所有设置了过期时间的键执行lur操作,没有设置过期时间的键不会淘汰
6.volatile-lfu
和allkeys-lfu一样,但是最会对所有设置了过期时间的键执行lfu操作,没有设置过期时间的键不会淘汰
7.volatile-random
和allkeys-random一样,但是只会随机淘汰设置了过期时间的键.
8.volatiile-ttl
根据键设置的过期时间进行淘汰,优先淘汰最近过期的键.
3.lru 算法一般实现
lru(least recently used)最近最少使用淘汰策略.
所有的key保存在一个map中,同时创建一个使用时间的双向链表根据访问时间进行对元素进行排序,访问key的时候使用map,这样可以保证在o(1)时间内访问元素.每当一个元素被访问的时候就立即更新链表中该元素对应的节点,把节点移动到链表的头节点,这样链表的尾节点肯定是最早使用的节点,淘汰的时候就可以直接从链表的尾部进行淘汰.
4.lfu 算法一般实现
lfu(least frequently used)最近不长使用算法.
一般实现:
和lru算法一样,把所有的key保存到一个map中,让key的访问速度为o(1),链表中节点的信息为key和当前key的访问次数,每当该key访问的时候就更新key对应的节点在链表中的位置,这样淘汰的时候也可以从队尾进行淘汰.但是因为所有的访问都会更新链表,而根据链表查找的时间复杂度为o(n)这将严重影响访问速度,所以下面提出了优化策略
优化:
不在使用一个链表保存key,而是所有访问次数相同的的key保存到同一个链表中,最新达到该访问次数的key放到链表的队尾,同时使用一个map保存所有的链表,map的key为访问次数,map的value为链表的头节点,然后另外保存链表map中key的最小值,当淘汰策略发生的时候使用,这样当访问一个key的时候更新链表的操作也是o(1)
5.redis的近似lru和lfu
redis没有使用lru算法,因为使用lru算法会额外占用大量的内存,同时也必须对现有数据结构做较大改动,所以redis采用了近似lru的算法,
redis为每个key增加了一个额外的24bit的字段,这个字段保存了上次访问的时间戳,当redis执行操作时如果发现超过了最大内存则会执行键的淘汰策略.如果时lru策略redis会从所有键中随机挑选5(通过maxmemory_samples可配置)个,然后淘汰掉最近最少使用的那个,如果还是操作了最大内存,则循环执行以上操作.这样做不会对当前数据结构做很大改动,也不会占用很大内存,在淘汰的时候也不需要进行遍历.但是这样做的缺点也很明显,就是会淘汰掉一些本来不应该淘汰的key.