edis淘汰策略
redis淘汰策略
1.redis淘汰策略
- 先说结论:Redis是使用
定期删除 + 惰性删除
两者配合的过期策略
过期策略
为什么要淘汰
- 一般情况下,当内存超出物理内存限制时,内存数据将与磁盘产生频繁交换(swap)
- swap会导致redis性能急剧下降,对于访问量较大的情况下,swap的存取效率会让服务基本处于不可用的状态。
- 在生产环境中,一般不允许redis出现swap行为,redis提供了 maxmemory 设置其最多可占用的内存空间。
- 当redis使用的内存超出maxmemory时,此时已经没有多余可用的内存空间,新的数据将无法写入
- redis提供了几种数据淘汰策略,用于清理数据,腾出空间以继续提供服务
定期删除(不推荐)
- 定期删除指的是Redis默认每隔100ms就
随机抽取 一些设置了过期时间的key,检测这些key是否过期,如果过期了就将其删掉
。 - 因为key太多,如果全盘扫描所有的key会非常耗性能,所以是随机抽取一些key来删除。这样就有可能删除不完,需要惰性删除配合。
惰性删除
- 惰性删除不再是Redis去主动删除,而是在
客户端要获取某个key的时候
,Redis会先去检测一下这个key是否已经过期
- 如果没有过期则返回给客户端,如果已经过期了,那么Redis会删除这个key,不会返回给客户端。
- 所以惰性删除可以解决一些过期了,但没被定期删除随机抽取到的key。
- 但有些过期的key既没有被随机抽取,也没有被客户端访问,就会一直保留在数据库,占用内存,长期下去可能会导致内存耗尽。
- 所以Redis提供了内存淘汰机制来解决这个问题。
为什么不使用定时删除?
所谓定时删除,指的是用一个定时器来负责监视key,当这个key过期就自动删除,虽然内存及时释放,但是十分消耗CPU资源,因此一般不推荐采用这一策略。
内存淘汰机制
当内存达到
maxmemory
后,Redis会按照maxmemory-policy
启动淘汰策略volatile开头的只会淘汰带有过期时间的key,allkeys则是所有的key
如果redis只是作为缓存使用,可以使用allkeys,如果有些数据是务必持久化的,则使用volatile
noeviction(默认策略): 不会删除任何数据,拒绝所有写入操作并返回客户端错误消息(error)OOM command not allowed when used memory,此时 Redis 只响应删和读操作;
allkeys-lru: 从所有 key 中使用 LRU 算法进行淘汰(LRU 算法:最近最少使用算法);
allkeys-lfu: 从所有 key 中使用 LFU 算法进行淘汰(LFU 算法:最不常用算法,根据使用频率计算,4.0 版本新增);
volatile-lru: 从设置了过期时间的 key 中使用 LRU 算法进行淘汰;
volatile-lfu: 从设置了过期时间的 key 中使用 LFU 算法进行淘汰;
allkeys-random: 从所有 key 中随机淘汰数据;
volatile-random: 从设置了过期时间的 key 中随机淘汰数据;
volatile-ttl: 在设置了过期时间的key中,淘汰过期时间剩余最短的。
持久化如何处理过期?
在持久化和数据恢复阶段,对过期key也有一些特殊的处理
RDB
- 从内存数据库持久化数据到RDB文件:持久化key之前,会检查是否过期,过期的key不进入RDB文件
- 从RDB文件恢复数据到内存数据库:数据载入数据库之前,会对key先进行过期检查,如果过期,不导入数据库(主库情况)。
AOF
从内存数据库持久化数据到AOF文件
:
- 当key过期后,
还没有被删除
,此时进行执行持久化操作(该key是不会进入aof文件的,因为没有发生修改命令) - 当key过期后,
在发生删除操作时
,程序会向aof文件追加一条del命令
(在将来的以aof文件恢复数据的时候该过期的键就会被删掉)
- 当key过期后,
AOF重写
:重写时,会先判断key是否过期,已过期的key不会重写到aof文件
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 访问频率随时间衰减;
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即可。