Redis内存回收淘汰策略
一、Redis最大内存
1.1 预估最大内存
Redis作为内存数据库,需要尽量把那些频繁被访问的热点数据放入Redis。
按照二八原则,20%的数据承载了80%的访问量,可以按照这个原则来预估实际的Redis内存大小,比如后台数据库的容量是10GB,那么Redis的容量可以设置为2GB。
二八原则是个常规的估算,有些具体情况还要具体对待:
- 比如针对电商的促销,可能热点数据只占全部全部商品数据的10%。
- 针对有个性化推荐的内容,20%的数据可能无法提供80%的访问量,可能需要40-50%的数据才行
- 还有一些场景,为了最大的性能,也有可能100%的数据全部放入redis,比如秒杀场景,可以把所有参与秒杀的商品数据全部放入redis。
确定了最大内存后就可以设置redis的最大内存。
1.2 设置最大内存
redis默认无限使用服务器内存,为了防止在极端情况下服务器内存被耗尽,redis需要设置最大内存。
使用命令
src/redis-cli config set maxmemory 4gb
maxmemory仅仅是指redis数据实际占用的内存(自身,数据,缓冲区),不包含碎片。
二、内存淘汰策略
2.1 删除过期键对象
惰性删除
惰性删除是在在客户端访问的时候check一下数据是否过期,如果过期则立即删除,不给客户端返回任何值。惰性删除可以降低删除对CPU的影响,但是如果一个key一直不被访问,那么这个key就有可能一直不会过期,最终会造成数据的浪费, 所以引入了定时任务删除。
定时任务删除
Redis内部维护的定时任务,执行流程:
- 随机选择20个key
- .如果过期的key的数量大于25%,则循环执行过期key删除
- 删除过程中如果发现超时(25毫秒),则进入快速回收模式:
- 删除的超时时间设为1毫秒
- 2 秒内只执行一次
定时任务的执行频率由hz参数控制,默认为10,表述一秒执行10次。
惰性任务和定时任务都有一定的随机性,最终还是有key一直执行不到,但是已经过期,这些key永远都不会被淘汰。所以redis还引入了相对比较精准的淘汰策略。
2.2 内存溢出控制策略
该值由下面参数控制。
maxmemory-policy
不淘汰策略
noeviction是redis的默认策略,当内存使用超过配置的时候会返回错误,不会删除任何键,如果有写请求进来,redis直接报错,不提供服务,但是读请求可以照常执行。
淘汰策略
基于过期时间key的淘汰策略:
1. volatile-lru:对于设置了过期时间的key,淘汰最久没有使用的key(LRU算法)
2. volatile-randow:对于设置了过期时间的key,随机选择key进行淘汰
3. volatile-ttl:对于设置了过期时间的key,淘汰过期时间最早的key
4. volatile-lfu:对于设置了过期时间的key,淘汰但使用频率最少的键(LFU算法)
所有key的淘汰策略:
1. volatile-lru:对于所有key,淘汰久没有使用的key(LRU算法)
2. volatile-randow:对于所有key,随机选择key进行淘汰
3. volatile-lfu:对于所有key,淘汰使用频率最少的键(LFU算法)
LRU( Least Recently Used)
按照最少使用原则进行数据筛选,最不常用的数据会被选中,频繁使用的数据会持续保留。
LRU会把所有数据组装成一个链表,表头代表最近使用的数据,表位表示最久没有使用的数据,用于淘汰。
- 新加入的数据放入表头
- 数据被访问后移到表头。
- 淘汰表尾
LRU的核心是要维护一个链表,对于有大量key的redis来说,太重了,链表的维护会影响redis的性能。
Redis对LRU做个改造:
-
RedisObject会记录每个对象的lru。
-
当有数据会淘汰时,redis会随机选择N个数据,作为一个集合。
-
针对这N个数据,redis会对比起lru,把lru最小的数据淘汰。
-
N的值redis提供了配置
maxmemory-samples
默认值为5。当配置为10时,改造的lru接近正式的lru,但是比较耗费cpu,建议保持默认即可。
-
当需要再次淘汰数据时,选择lru小于 < min(第一次初始化的集合的lru) 的数据进入集合,然后再基于lru进行淘汰。
配置建议:
- 优先allkeys-lru:充分使用lru算法,尤其当数据冷热比较明显时,最大限度的保证热点数据补被淘汰。
- 没有明显的冷热,可以选择allkeys-random,随机过期即可。
- 如果数据有长期不过期的数据,这些数据可以不设置过期时间,建议使用voliate-lru。这样没有过期时间的数据不会被淘汰,而其他数据可以享受lru算法进行淘汰。
三、内存淘汰流程以及影响
如果设置了maxmemory,redis在每次执行任务时候都会尝试内存回收,当redis一直工作在内存溢出状态(used_memory > maxmemory)下,且不是noeviction策略时,会频繁的触发回收内存的操作,影响redis的性能。
频繁回收内存成本很高:
- 查找可以回收的key
- 删除key
- 主从模式下,将删除命令slave。
流程
建议在生产环境redis的工作在maxmemory > used_memory的状态下,尽量避免频繁的内存回收开销。