• 欢迎访问蜷缩的蜗牛博客 蜷缩的蜗牛
  • 微信搜索: 蜷缩的蜗牛 | 联系站长 kbsonlong@qq.com
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

关于Redis的数据清理

大数据 lxw1234@qq.com 1年前 (2017-07-26) 23次浏览 已收录

我们数据平台中有使用 Redis 来给线上提供低延时(20 毫秒以内)的高并发读写请求,其中最大的 Redis 使用了阿里云的 Redis 集群(256G),存储的记录超过 10 亿,Key 的有效期设置为 15 天,每天写入的记录大概 5000 万左右,QPS 大概在 6 万左右。由于过期 Key 的产生速度大于 Redis 自动清理的速度,因此在 Redis 中会有大量过期 Key 未被及时清理。

为什么有过期的 Key 未被清理呢?这个得先熟悉一下 Redis 的删除策略。

Redis 常用的删除策略有以下三种:

  • 被动删除(惰性删除):当读/写一个已经过期的 Key 时,会触发惰性删除策略,直接删除掉这个 Key;
  • 主动删除(定期删除):Redis 会定期巡检,来清理过期 Key;
  • 当内存达到 maxmemory 配置时候,会触发 Key 的删除操作;

另外,还有一种基于触发器的删除策略,因为对 Redis 压力太大,一般没人使用。

这里先介绍后两种删除策略(网上有很多说明)。

参考:http://www.cnblogs.com/chenpingzhao/p/5022467.html

主动删除(定期删除)

在 Redis 中,常规操作由 redis.c/serverCron 实现,它主要执行以下操作:

  • 更新服务器的各类统计信息,比如时间、内存占用、数据库占用情况等。
  • 清理数据库中的过期键值对。
  • 对不合理的数据库进行大小调整。
  • 关闭和清理连接失效的客户端。
  • 尝试进行 AOF 或 RDB 持久化操作。
  • 如果服务器是主节点的话,对附属节点进行定期同步。
  • 如果处于集群模式的话,对集群进行定期同步和连接测试。

Redis 将 serverCron 作为时间事件来运行,从而确保它每隔一段时间就会自动运行一次, 又因为 serverCron 需要在 Redis 服务器运行期间一直定期运行, 所以它是一个循环时间事件:serverCron 会一直定期执行,直到服务器关闭为止。

在 Redis 2.6 版本中, 程序规定 serverCron 每秒运行 10 次, 平均每 100 毫秒运行一次。 从 Redis 2.8 开始, 用户可以通过修改 hz 选项来调整 serverCron 的每秒执行次数, 具体信息请参考 redis.conf 文件中关于 hz 选项的说明。


也叫定时删除,这里的“定期”指的是 Redis 定期触发的清理策略,由位于 src/redis.c 的 activeExpireCycle(void)函数来完成。

serverCron 是由 redis 的事件框架驱动的定位任务,这个定时任务中会调用 activeExpireCycle 函数,针对每个 db 在限制的时间 REDIS_EXPIRELOOKUPS_TIME_LIMIT 内迟可能多的删除过期 key,之所以要限制时间是为了防止过长时间 的阻塞影响 redis 的正常运行。这种主动删除策略弥补了被动删除策略在内存上的不友好。

因此,Redis 会周期性的随机测试一批设置了过期时间的 key 并进行处理。测试到的已过期的 key 将被删除。典型的方式为,Redis 每秒做 10 次如下的步骤:

  • 随机测试 100 个设置了过期时间的 key
  • 删除所有发现的已过期的 key
  • 若删除的 key 超过 25 个则重复步骤 1

这是一个基于概率的简单算法,基本的假设是抽出的样本能够代表整个 key 空间,redis 持续清理过期的数据直至将要过期的 key 的百分比降到了 25%以下。这也意味着在任何给定的时刻已经过期但仍占据着内存空间的 key 的量最多为每秒的写操作量除以 4.

Redis-3.0.0 中的默认值是 10,代表每秒钟调用 10 次后台任务。

除了主动淘汰的频率外,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;

hz 调大将会提高 Redis 主动淘汰的频率,如果你的 Redis 存储中包含很多冷数据占用内存过大的话,可以考虑将这个值调大,但 Redis 作者建议这个值不要超过 100。我们实际线上将这个值调大到 100,观察到 CPU 会增加 2%左右,但对冷数据的内存释放速度确实有明显的提高(通过观察 keyspace 个数和 used_memory 大小)。

可以看出 timelimit 和 server.hz 是一个倒数的关系,也就是说 hz 配置越大,timelimit 就越小。换句话说是每秒钟期望的主动淘汰频率越高,则每次淘汰最长占用时间就越短。这里每秒钟的最长淘汰占用时间是固定的 250ms(1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/100),而淘汰频率和每次淘汰的最长时间是通过 hz 参数控制的。

从以上的分析看,当 redis 中的过期 key 比率没有超过 25%之前,提高 hz 可以明显提高扫描 key 的最小个数。假设 hz 为 10,则一秒内最少扫描 200 个 key(一秒调用 10 次*每次最少随机取出 20 个 key),如果 hz 改为 100,则一秒内最少扫描 2000 个 key;另一方面,如果过期 key 比率超过 25%,则扫描 key 的个数无上限,但是 cpu 时间每秒钟最多占用 250ms。

当 REDIS 运行在主从模式时,只有主结点才会执行上述这两种过期删除策略,然后把删除操作”del key”同步到从结点。

maxmemory

当前已用内存超过 maxmemory 限定时,触发主动清理策略,这些策略可以配置(参数 maxmemory-policy),包括以下几个:

 

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据

当 mem_used 内存已经超过 maxmemory 的设定,对于所有的读写请求,都会触发 redis.c/freeMemoryIfNeeded(void)函数以清理超出的内存。注意这个清理过程是阻塞的,直到清理出足够的内存空间。所以如果在达到 maxmemory 并且调用方还在不断写入的情况下,可能会反复触发主动清理策略,导致请求会有一定的延迟。

 

清理时会根据用户配置的 maxmemory-policy 来做适当的清理(一般是 LRU 或 TTL),这里的 LRU 或 TTL 策略并不是针对 redis 的所有 key,而是以配置文件中的 maxmemory-samples 个 key 作为样本池进行抽样清理。

总结与备忘

如果 Redis 中每天过期大量 Key(比如几千万),那么必须得考虑过期 Key 的清理:

增加 Redis 主动清理的频率(通过调大 hz 参数);

手动清理过期 Key,最简单的方法是进行 scan 操作,scan 操作会触发第一种被动删除,scan 操作时候别忘了加 count;

dbsize 命令返回的 Key 数量,包含了过期 Key;

randomkey 命令返回的 Key,不包含过期 Key;

scan 命令返回的 Key,包含过期 Key;

info 命令返回的# Keyspace:

db6:keys=1034937352,expires=994731489,avg_ttl=507838502

keys 对应的 Key 数量等同于 dbsize;

expires 指的是设置了过期时间的 Key 数量;

avg_ttl 指设置了过期时间的 Key 的平均过期时间(单位:毫秒);

 

 

如果觉得本博客对您有帮助,请 赞助作者

转载请注明:lxw 的大数据田地 » 关于 Redis 的数据清理


蜷缩的蜗牛 , 版权所有丨如未注明 , 均为原创丨 转载请注明关于 Redis 的数据清理
喜欢 (0)
[]
分享 (0)