共计 2612 个字符,预计需要花费 7 分钟才能阅读完成。
最近线上遇到 Redis 内存达到 maxmemory 限制后,数据淘汰过慢导致拖慢应用请求的问题。后来仔细看了一下 Redis 的各种数据淘汰策略,总结一下。
首先,Redis 有三种删除 key 的时机,它们对应不同的淘汰策略:
1. 当读 / 写一个已经过期的 key 时,会触发惰性删除策略,直接删除掉这个过期 key。
2. 由于惰性删除策略无法保证冷数据被及时删掉,所以 Redis 会定期主动淘汰一批已过期的 key。
3. 当前已用内存超过 maxmemory 限定时,触发主动清理策略。
下面详细说一下定期主动淘汰策略和主动清理策略,以及它们所对应的配置参数的含义。
• 定期主动淘汰策略
首先,这里的“定期”指的是 Redis 定期调用 databasesCron()函数时触发的清理策略,这个定期的频率由配置文件中的 hz 参数决定,代表了一秒钟内,后台任务期望被调用的次数。Redis-3.0.0 中的默认值是 10,代表每秒钟调用 10 次后台任务。
hz 调大将会提高 Redis 主动淘汰的频率,如果你的 Redis 存储中包含很多冷数据占用内存过大的话,可以考虑将这个值调大,但 Redis 作者建议这个值不要超过 100。我们实际线上将这个值调大到 100,观察到 CPU 会增加 2% 左右,但对冷数据的内存释放速度确实有明显的提高(通过观察 keyspace 个数和 used_memory 大小)。
除了主动淘汰的频率外,Redis 对每次淘汰任务执行的最大时长也有一个限定,这样保证了每次主动淘汰不会过多阻塞应用请求,以下是这个限定计算公式:
#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* CPU max % for keys collection */
…
timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;
可以看出 timelimit 和 server.hz 是一个倒数的关系,也就是说 hz 配置越大,timelimit 就越小。换句话说是每秒钟期望的主动淘汰频率越高,则每次淘汰最长占用时间就越短。这里每秒钟的最长淘汰占用时间是固定的 250ms(1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/100),而淘汰频率和每次淘汰的最长时间是通过 hz 参数控制的。
具体的淘汰过程为:在 redis.c/activeExpireCycle()函数中,针对每个 db 有一个循环,每次从 db->expires 集合中随机取出 20 个 key,如果有超过 5 个 key 过期淘汰,则继续循环。也就是说如果超过 25% 的 key 过期淘汰,则继续淘汰,直到随机抽取的 key 的过期率低于 25%,或者整个循环时间超过 timelimit 的最大限定。则结束整个淘汰过程。
从以上的分析看,当 redis 中的过期 key 比率没有超过 25% 之前,提高 hz 可以明显提高扫描 key 的最小个数。假设 hz 为 10,则一秒内最少扫描 200 个 key(一秒调用 10 次 * 每次最少随机取出 20 个 key),如果 hz 改为 100,则一秒内最少扫描 2000 个 key;另一方面,如果过期 key 比率超过 25%,则扫描 key 的个数无上限,但是 cpu 时间每秒钟最多占用 250ms。
• maxmemory 的主动清理策略
当 mem_used 内存已经超过 maxmemory 的设定,对于所有的读写请求,都会触发 redis.c/freeMemoryIfNeeded(void)函数以清理超出的内存。注意这个清理过程是阻塞的,直到清理出足够的内存空间。所以如果在达到 maxmemory 并且调用方还在不断写入的情况下,可能会反复触发主动清理策略,导致请求会有一定的延迟。
清理时会根据用户配置的 maxmemory-policy 来做适当的清理(一般是 LRU 或 TTL),这里的 LRU 或 TTL 策略并不是针对 redis 的所有 key,而是以配置文件中的 maxmemory-samples 个 key 作为样本池进行抽样清理。
maxmemory-samples 在 redis-3.0.0 中的默认配置为 5,如果增加,会提高 LRU 或 TTL 的精准度,redis 作者测试的结果是当这个配置为 10 时已经非常接近全量 LRU 的精准度了,并且增加 maxmemory-samples 会导致在主动清理时消耗更多的 CPU 时间。
建议:
尽量不要触发 maxmemory,最好在 mem_used 内存占用达到 maxmemory 的一定比例后,需要考虑调大 hz 以加快淘汰,或者进行集群扩容。
如果能够控制住内存,则可以不用修改 maxmemory-samples 配置;如果 Redis 本身就作为 LRU cache 服务(这种服务一般长时间处于 maxmemory 状态,由 Redis 自动做 LRU 淘汰),可以适当调大 maxmemory-samples。
下面关于 Redis 的文章您也可能喜欢,不妨参考下:
Ubuntu 14.04 下 Redis 安装及简单测试 http://www.linuxidc.com/Linux/2014-05/101544.htm
Redis 主从复制基本配置 http://www.linuxidc.com/Linux/2015-03/115610.htm
Redis 集群明细文档 http://www.linuxidc.com/Linux/2013-09/90118.htm
Ubuntu 12.10 下安装 Redis(图文详解)+ Jedis 连接 Redis http://www.linuxidc.com/Linux/2013-06/85816.htm
Redis 系列 - 安装部署维护篇 http://www.linuxidc.com/Linux/2012-12/75627.htm
CentOS 6.3 安装 Redis http://www.linuxidc.com/Linux/2012-12/75314.htm
Redis 安装部署学习笔记 http://www.linuxidc.com/Linux/2014-07/104306.htm
Redis 配置文件 redis.conf 详解 http://www.linuxidc.com/Linux/2013-11/92524.htm
Redis 的详细介绍:请点这里
Redis 的下载地址:请点这里
本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-05/131836.htm