15.Redis淘汰策略
# 01.Redis淘汰策略
- 先说结论:Redis是使用
定期删除 + 惰性删除
两者配合的过期策略
# 1.1 过期策略
# 1、为什么要淘汰
一般情况下,当内存超出物理内存限制时,内存数据将与磁盘产生频繁交换(swap)
swap会导致Redis性能急剧下降,对于访问量较大的情况下,swap的存取效率会让服务基本处于不可用的状态
在生产环境中,一般不允许Redis出现swap行为,Redis提供了 maxmemory 设置其最多可占用的内存空间
当Redis使用的内存超出maxmemory时,此时已经没有多余可用的内存空间,新的数据将无法写入
Redis提供了几种数据淘汰策略,用于清理数据,腾出空间以继续提供服务
# 2、定期删除(不推荐)
定期删除指的是Redis默认每隔100ms就
随机抽取 一些设置了过期时间的key,检测这些key是否过期,如果过期了就将其删掉
因为key太多,如果全盘扫描所有的key会非常耗性能,所以是随机抽取一些key来删除这样就有可能删除不完,需要惰性删除配合
# 3、惰性删除
惰性删除不再是Redis去主动删除,而是在
客户端要获取某个key的时候
,Redis会先去检测一下这个key是否已经过期
如果没有过期则返回给客户端,如果已经过期了,那么Redis会删除这个key,不会返回给客户端
所以惰性删除可以解决一些过期了,但没被定期删除随机抽取到的key
但有些过期的key既没有被随机抽取,也没有被客户端访问,就会一直保留在数据库,占用内存,长期下去可能会导致内存耗尽
所以Redis提供了内存淘汰机制来解决这个问题
为什么不使用定时删除?
所谓定时删除,指的是用一个定时器来负责监视key,当这个key过期就自动删除,虽然内存及时释放,但是十分消耗CPU资源,因此一般不推荐采用这一策略
# 1.2 内存淘汰机制
- 当内存达到
maxmemory
后,Redis会按照maxmemory-policy
启动淘汰策略 volatile开头的只会淘汰带有过期时间的key,allkeys则是所有的key
- 如果Redis只是作为缓存使用,可以使用allkeys,如果有些数据是务必持久化的,则使用volatile
maxmemory-policy | 含义 | 特性 | |
---|---|---|---|
1 | noeviction Redis 默认策略 | 不淘汰 | 内存超限后写命令会返回错误 (如OOM, del命令除外)即可读不可写,该策略不会丢失数据,是默认策略 |
2 | volatile-lru | 易失key的LRU | 淘汰具有过期时间的key ,最少使用的key优先淘汰,没有过期时间的key不会被淘汰 ,该策略可以保证持久化的数据不被丢失 |
3 | volatile-ttl | 易失key的TTL淘汰 | 与 2 类似,区别是比较过期时间ttl的值,值越小越优先淘汰 |
4 | volatile-random | 易失Key的随机 | 与 2、3 类似,区别是随机淘汰具备过期时间的key 不分使用频率和过期时间长短 |
5 | allkeys-lru | 所有key的LRU机制 | 与 2 类似,不过该淘汰策略范围是Redis中的所有key ,不区分是否有过期时间,但是 区分使用频率 |
6 | allkeys-random | 所有key随机淘汰 | 与 5 类似,范围是所有的key,但是不区分使用频率 |
# 1.3 持久化如何处理过期?
在持久化和数据恢复阶段,对过期key也有一些特殊的处理
RDB
- 从内存数据库持久化数据到RDB文件:持久化key之前,会检查是否过期,过期的key不进入RDB文件
- 从RDB文件恢复数据到内存数据库:数据载入数据库之前,会对key先进行过期检查,如果过期,不导入数据库(主库情况)
AOF
从内存数据库持久化数据到AOF文件
:- 当key过期后,
还没有被删除
,此时进行执行持久化操作(该key是不会进入aof文件的,因为没有发生修改命令) - 当key过期后,
在发生删除操作时
,程序会向aof文件追加一条del命令
(在将来的以aof文件恢复数据的时候该过期的键就会被删掉)
- 当key过期后,
AOF重写
:重写时,会先判断key是否过期,已过期的key不会重写到aof文件
# 1.4 Redis的LRU
传统LRU算法弊端
- 传统的LRU是使用栈的形式,每次都将最新使用的移入栈顶
- 但是用栈的形式会导致执行select *的时候大量非热点数据占领头部数据,所以需要改进
Redis3.0中LRU算法
- Redis每次按key获取一个值的时候,都会更新value中的lru字段为当前秒级别的时间戳
- Redis初始的实现算法很简单,随机从dict中取出五个key,淘汰一个lru字段值最小的
- 在3.0的时候,又改进了一版算法,
首先第一次随机选取的key都会放入一个pool中(pool的大小为16)
,pool中的key是按lru大小顺序排列的 - 接下来每次随机选取的
key lru值必须小于pool中最小的lru才会继续放入
,直到将pool放满 - 放满之后,每次如果有
新的key需要放入,需要将pool中lru最大的一个key取出
- 淘汰的时候,
直接从pool中选取一个lru最小的值然后将其淘汰
Redis 4.0中新的LFU算法
- 在LRU中,某个键很少被访问,但在
刚刚被访问后其被淘汰概率很低
,从而出现这类异常持续存在的缓存 而LFU中,按访问频次淘汰最少被访问的键
,LFU 使用 Morris counter 概率计数器- volatile-lfu:设置过期时间的键按LFU淘汰
- allkeys-lfu:所有键按LFU淘汰
- Morris counter 概率计数器
- LOG_C 存储的是访问频率,不是访问次数;
- LOG_C 访问频率随时间衰减;
# 1.5 Redis如何发现热点key
凭借经验,进行预估:例如提前知道了某个活动的开启,那么就将此Key作为热点Key
服务端收集:在操作Redis之前,加入一行代码进行数据统计
抓包进行评估:Redis使用TCP协议与客户端进行通信,通信协议采用的是RESP,所以自己写程序监听端口也能进行拦截包进行解析
在proxy层,对每一个 Redis 请求进行收集上报
Redis自带命令查询:Redis4.0.4版本提供了Redis-cli –hotkeys就能找出热点Key
- 如果要用Redis自带命令查询时,要注意需要先把内存逐出策略设置为allkeys-lfu或者volatile-lfu,否则会返回错误
- 进入Redis中使用config set maxmemory-policy allkeys-lfu即可