Redis中删除过时Key的三种策略

转载自:http://blog.csdn.net/a_bang/article/details/52986935?locationNum=9&fps=1 html

 

项目中有个接口要频繁调用查询数据库中的数据,为了下降数据库的压力,因此把一部分记录先缓存在redis中,对redis中的数据设置了期限。今天无心间发现一个问题,使用dbsize查询出来的数量,比实际缓存量要高一部分。用redis

redis-cli keys '*'|wc -l

获取到的数据和实际状况是同样的。以下面两图:算法

 

对比发现,redis中key的总量为286957,比数据库中的264032高出了20000多个!为何会这样呢?查找程序缘由,并无发现逻辑问题。查找redis相关资料,发现原来是redis对过时键处理机制致使的偏差。 dbsize返回的是包含过时键的总数,因此形成了偏差!结合查找的资料,拿来一块儿分享。数据库

Redis对于过时键有三种清除策略缓存

  • 被动删除:当读/写一个已通过期的key时,会触发惰性删除策略,直接删除掉这个过时key
  • 主动删除:因为惰性删除策略没法保证冷数据被及时删掉,因此Redis会按期主动淘汰一批已过时的key
  • 当前已用内存超过maxmemory限定时,触发主动清理策略

被动删除服务器

只有key被操做时(如GET),REDIS才会被动检查该key是否过时,若是过时则删除之而且返回NIL。 一、这种删除策略对CPU是友好的,删除操做只有在不得不的状况下才会进行,不会对其余的expire key上浪费无谓的CPU时间。 二、可是这种策略对内存不友好,一个key已通过期,可是在它被操做以前不会被删除,仍然占据内存空间。若是有大量的过时键存在可是又不多被访问到,那会形成大量的内存空间浪费。expireIfNeeded(redisDb *db, robj *key)函数位于src/db.c。 但仅是这样是不够的,由于可能存在一些key永远不会被再次访问到,这些设置了过时时间的key也是须要在过时后被删除的,咱们甚至能够将这种状况看做是一种内存泄露—-无用的垃圾数据占用了大量的内存,而服务器却不会本身去释放它们,这对于运行状态很是依赖于内存的Redis服务器来讲,确定不是一个好消息。框架

主动删除dom

先说一下时间事件,对于持续运行的服务器来讲, 服务器须要按期对自身的资源和状态进行必要的检查和整理, 从而让服务器维持在一个健康稳定的状态, 这类操做被统称为常规操做(cron job)函数

在 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次以下的步骤:

  1. 随机测试100个设置了过时时间的key
  2. 删除全部发现的已过时的key
  3. 若删除的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限定时,触发主动清理策略

  • volatile-lru:只对设置了过时时间的key进行LRU(默认值)
  • allkeys-lru : 删除lru算法的key
  • volatile-random:随机删除即将过时key
  • allkeys-random:随机删除
  • volatile-ttl : 删除即将过时的
  • noeviction : 永不过时,返回错误

当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根本就不会准确的将整个数据库中最久未被使用的键删除,而是每次从数据库中随机取5个键并删除这5个键里最久未被使用的键。上面提到的全部的随机的操做实际上都是这样的,这个5能够用过redis的配置文件中的maxmemeory-samples参数配置。

Replication link和AOF文件中的过时处理

为了得到正确的行为而不至于致使一致性问题,当一个key过时时DEL操做将被记录在AOF文件并传递到全部相关的slave。也即过时删除操做统一在master实例中进行并向下传递,而不是各salve各自掌控。这样一来便不会出现数据不一致的情形。当slave链接到master后并不能当即清理已过时的key(须要等待由master传递过来的DEL操做),slave仍需对数据集中的过时状态进行管理维护以便于在slave被提高为master会能像master同样独立的进行过时处理。

参考博文:

http://www.cnblogs.com/chenpingzhao/p/5022467.html?utm_source=tuicool&utm_medium=referral

相关文章
相关标签/搜索