转载自: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被操做时(如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 实现, 它主要执行如下操做测试
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次以下的步骤:
这是一个基于几率的简单算法,基本的假设是抽出的样本可以表明整个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限定时,触发主动清理策略
当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时间,建议:
这里提一句,实际上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