Redis中键的过期删除策略深入讲解

如果一个键过期了,那么它什么时候会被删除呢?

这个问题有三种可能的答案,它们分别代表了三种不同的删除策略:

  • 定时删除:在设置键的过期时间的同时,创建一个定时器( timer ). 让定时器在键的过期时间来临时,立即执行对键的删除操作。
  • 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。
  • 定期删除: 每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库, 则由算法决定。

在这三种策略中,第一种和第三种为主动删除策略, 而第二种则为被动删除策略。

前言

使用Redis时我们可以使用EXPIRE或EXPIREAT命令给key设置过期删除时间,结构体redisDb中的expires字典保存了所有key的过期时间,这个字典(dict)的key是一个指针,指向redis中的某个key对象,过期字典的value是一个保存过期时间的整数。

/* Redis database representation. There are multiple databases identified
 * by integers from 0 (the default database) up to the max configured
 * database. The database number is the 'id' field in the structure. */
typedef struct redisDb {
 dict *dict;     /* The keyspace for this DB */
 dict *expires;    /* 过期字典*/
 dict *blocking_keys;  /* Keys with clients waiting for data (BLPOP) */
 dict *ready_keys;   /* Blocked keys that received a PUSH */
 dict *watched_keys;   /* WATCHED keys for MULTI/EXEC CAS */
 struct evictionPoolEntry *eviction_pool; /* Eviction pool of keys */
 int id;      /* Database ID */
 long long avg_ttl;   /* Average TTL, just for stats */
} redisDb;

设置过期时间

不论是EXPIRE,EXPIREAT,还是PEXPIRE,PEXPIREAT,底层的具体实现是一样的。在Redis的key空间中找到要设置过期时间的这个key,然后将这个entry(key的指针,过期时间)加入到过期字典中。

void setExpire(redisDb *db, robj *key, long long when) {
 dictEntry *kde, *de;

 /* Reuse the sds from the main dict in the expire dict */
 kde = dictFind(db->dict,key->ptr);
 redisAssertWithInfo(NULL,key,kde != NULL);
 de = dictReplaceRaw(db->expires,dictGetKey(kde));
 dictSetSignedIntegerVal(de,when);
}

过期删除策略

如果一个key过期了,何时会被删除呢?在Redis中有两种过期删除策略:(1)惰性过期删除;(2)定期删除。接下来具体看看。

惰性过期删除

Redis在执行任何读写命令时都会先找到这个key,惰性删除就作为一个切入点放在查找key之前,如果key过期了就删除这个key。

robj *lookupKeyRead(redisDb *db, robj *key) {
 robj *val;

 expireIfNeeded(db,key); // 切入点
 val = lookupKey(db,key);
 if (val == NULL)
  server.stat_keyspace_misses++;
 else
  server.stat_keyspace_hits++;
 return val;
}

定期删除

key的定期删除会在Redis的周期性执行任务(serverCron,默认每100ms执行一次)中进行,而且是发生Redis的master节点,因为slave节点会通过主节点的DEL命令同步过来达到删除key的目的。

依次遍历每个db(默认配置数是16),针对每个db,每次循环随机选择20个(ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)key判断是否过期,如果一轮所选的key少于25%过期,则终止迭次,此外在迭代过程中如果超过了一定的时间限制则终止过期删除这一过程。

for (j = 0; j < dbs_per_call; j++) {
 int expired;
 redisDb *db = server.db+(current_db % server.dbnum);

 /* Increment the DB now so we are sure if we run out of time
  * in the current DB we'll restart from the next. This allows to
  * distribute the time evenly across DBs. */
 current_db++;

 /* Continue to expire if at the end of the cycle more than 25%
  * of the keys were expired. */
 do {
  unsigned long num, slots;
  long long now, ttl_sum;
  int ttl_samples;

  /* 如果该db没有设置过期key,则继续看下个db*/
  if ((num = dictSize(db->expires)) == 0) {
   db->avg_ttl = 0;
   break;
  }
  slots = dictSlots(db->expires);
  now = mstime();

  /* When there are less than 1% filled slots getting random
   * keys is expensive, so stop here waiting for better times...
   * The dictionary will be resized asap. */
  if (num && slots > DICT_HT_INITIAL_SIZE &&
   (num*100/slots < 1)) break;

  /* The main collection cycle. Sample random keys among keys
   * with an expire set, checking for expired ones. */
  expired = 0;
  ttl_sum = 0;
  ttl_samples = 0;

  if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)
   num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;// 20

  while (num--) {
   dictEntry *de;
   long long ttl;

   if ((de = dictGetRandomKey(db->expires)) == NULL) break;
   ttl = dictGetSignedIntegerVal(de)-now;
   if (activeExpireCycleTryExpire(db,de,now)) expired++;
   if (ttl > 0) {
    /* We want the average TTL of keys yet not expired. */
    ttl_sum += ttl;
    ttl_samples++;
   }
  }

  /* Update the average TTL stats for this database. */
  if (ttl_samples) {
   long long avg_ttl = ttl_sum/ttl_samples;

   /* Do a simple running average with a few samples.
    * We just use the current estimate with a weight of 2%
    * and the previous estimate with a weight of 98%. */
   if (db->avg_ttl == 0) db->avg_ttl = avg_ttl;
   db->avg_ttl = (db->avg_ttl/50)*49 + (avg_ttl/50);
  }

  /* We can't block forever here even if there are many keys to
   * expire. So after a given amount of milliseconds return to the
   * caller waiting for the other active expire cycle. */
  iteration++;
  if ((iteration & 0xf) == 0) { /* 每迭代16次检查一次 */
   long long elapsed = ustime()-start;

   latencyAddSampleIfNeeded("expire-cycle",elapsed/1000);
   if (elapsed > timelimit) timelimit_exit = 1;
  }
 // 超过时间限制则退出
  if (timelimit_exit) return;
  /* 在当前db中,如果少于25%的key过期,则停止继续删除过期key */
 } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);
}

总结

惰性删除:读写之前判断key是否过期

定期删除:定期抽样key,判断是否过期

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 关于redis Key淘汰策略的实现方法

    1 配置文件中的最大内存删除策略 在redis的配置文件中,可以设置redis内存使用的最大值,当redis使用内存达到最大值时(如何知道已达到最大值?),redis会根据配置文件中的策略选取要删除的key,并删除这些key-value的值.若根据配置的策略,没有符合策略的key,也就是说内存已经容不下新的key-value了,但此时有不能删除key,那么这时候写的话,将会出现写错误. 1.1 最大内存参数设置 若maxmemory参数设置为0,则分两种情况: *在64位系统上,表示没有限制.

  • Redis中LRU淘汰策略的深入分析

    前言 Redis作为缓存使用时,一些场景下要考虑内存的空间消耗问题.Redis会删除过期键以释放空间,过期键的删除策略有两种: 惰性删除:每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键:如果没有过期,就返回该键. 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键. 另外,Redis也可以开启LRU功能来自动淘汰一些键值对. LRU算法 当需要从缓存中淘汰数据时,我们希望能淘汰那些将来不可能再被使用的数据,保留那些将来还会频繁访问的数据,但最大的问题是

  • 浅谈redis的maxmemory设置以及淘汰策略

    redis的maxmemory参数用于控制redis可使用的最大内存容量.如果超过maxmemory的值,就会动用淘汰策略来处理expaire字典中的键. 关于redis的淘汰策略: Redis提供了下面几种淘汰策略供用户选择,其中默认的策略为noeviction策略: ·   noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错. ·   allkeys-lru:在主键空间中,优先移除最近未使用的key. ·   volatile-lru:在设置了过期时间的键空间中,优

  • Redis的LRU机制介绍

    在Redis中,如果设置的maxmemory,那就要配置key的回收机制参数maxmemory-policy,默认volatile-lru,参阅Redis作者的原博客:antirez weblog >> Redis as an LRU cache 原文中写得很清楚: 复制代码 代码如下: Another way to use Redis as a cache is the maxmemory directive, a feature that allows specifying a maxim

  • Redis中的数据过期策略详解

    1.Redis中key的的过期时间 通过EXPIRE key seconds命令来设置数据的过期时间.返回1表明设置成功,返回0表明key不存在或者不能成功设置过期时间.在key上设置了过期时间后key将在指定的秒数后被自动删除.被指定了过期时间的key在Redis中被称为是不稳定的. 当key被DEL命令删除或者被SET.GETSET命令重置后与之关联的过期时间会被清除 127.0.0.1:6379> setex s 20 1 OK 127.0.0.1:6379> ttl s (intege

  • Redis中键的过期删除策略深入讲解

    如果一个键过期了,那么它什么时候会被删除呢? 这个问题有三种可能的答案,它们分别代表了三种不同的删除策略: 定时删除:在设置键的过期时间的同时,创建一个定时器( timer ). 让定时器在键的过期时间来临时,立即执行对键的删除操作. 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键. 定期删除: 每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键.至于要删除多少过期键,以及要检查多少个数据库, 则由算法决定

  • Redis中键值过期操作示例详解

    1.过期设置 Redis 中设置过期时间主要通过以下四种方式: expire key seconds:设置 key 在 n 秒后过期: pexpire key milliseconds:设置 key 在 n 毫秒后过期: expireat key timestamp:设置 key 在某个时间戳(精确到秒)之后过期: pexpireat key millisecondsTimestamp:设置 key 在某个时间戳(精确到毫秒)之后过期: 下面分别来看以上这些命令的具体实现. 1)expire:N

  • 浅谈Redis 中的过期删除策略和内存淘汰机制

    目录 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 内存淘汰触发的最大内存 有哪些内存淘汰策略 内存淘汰算法 LRU LFU 为什么数据删除后内存占用还是很高 内存碎片如何产生 碎片率的意义 如何清理内存碎片 总结 参考 前言 Redis 中的 key 设置一个过期时间,在过期时间到的时候,Redis 是如何清除这个 key 的呢? 这来分析下 Redis 中的过期删除策略和内存淘

  • 浅谈Redis 中的过期删除策略和内存淘汰机制

    目录 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 内存淘汰触发的最大内存 有哪些内存淘汰策略 内存淘汰算法 LRU LFU 为什么数据删除后内存占用还是很高 内存碎片如何产生 碎片率的意义 如何清理内存碎片 总结 参考 前言 Redis 中的 key 设置一个过期时间,在过期时间到的时候,Redis 是如何清除这个 key 的呢? 这来分析下 Redis 中的过期删除策略和内存淘

  • Redis过期删除策略与内存淘汰策略

    目录 过期删除策略 设置Redis中key的过期时间 (单位:秒) 常见的三种过期删除策略 Redis使用用的过期删除策略 Redis的定期删除的流程 内存淘汰策略 设置Redis最大运行内存 Redis 内存淘汰策略有哪些? LRU 算法和 LFU 算法有什么区别? Redis 是如何实现 LRU 算法的? 什么是 LFU 算法? Redis 是如何实现 LFU 算法的? 过期删除策略 过期删除策略: redis可以对key设置过期时间,因此要有相应的机制将已过期的键值对删除. 设置Redis

  • Redis 的内存淘汰策略和过期删除策略的区别

    目录 前言 过期删除策略 如何设置过期时间? 如何判定 key 已过期了? 过期删除策略有哪些? Redis 过期删除策略是什么? 内存淘汰策略 如何设置 Redis 最大运行内存? Redis 内存淘汰策略有哪些? LRU 算法和 LFU 算法有什么区别? 总结 前言 Redis 是可以对 key 设置过期时间的,因此需要有相应的机制将已过期的键值对删除,而做这个工作的就是过期键值删除策略. Redis 的「内存淘汰策略」和「过期删除策略」,很多小伙伴容易混淆,这两个机制虽然都是做删除的操作,

  • Redis中键和数据库通用指令详解

    目录 一.Redis键(key)通用指令 1.key基本操作 2.时效性控制 3.查询模式 4.其它操作 二.数据库通用指令 1.基本操作 2.相关操作 一.Redis键(key)通用指令 可以参考菜鸟教程:Redis 键命令用于管理 redis 的键 key特征:key是一个字符串,通过key获取redis中保存的数据. 1.key基本操作 命令 功能 del key 该命令用于在 key 存在时删除 key exists key 检查给定 key 是否存在 type key 返回 key 所

  • Redis中AOF与RDB持久化策略深入分析

    目录 写在前面 一.Redis为什么要持久化 二.Redis的持久化方式 2.1. AOF持久化(Append of file) 2.1.1 fsync 系统调用 2.1.2 AOF持久化策略 2.1.3 aof_rewrite 2.2 RDB快照(redis默认持久化方式) 触发方式(自动触发和非自动触发) 2.3 RDB和AOF混用 2.4 三种持久化方式比较 三.什么是大key以及大key对持久化的影响 3.1 什么是大key 3.2 fork进程写时复制原理 3.3 面试题-大key对持

  • 浅谈Redis中的自动过期机制

    目录 Redis中的自动过期机制 一.使用Redis Key自动过期机制 二.SpringBoot整合key失效监听 Redis中的自动过期机制 实现需求:处理订单过期自动取消,比如下单30分钟未支付自动更改订单状态 1.使用Redis Key自动过期出发事件通知2.使用定时任务30分钟后检查3.按照每分钟轮训检查 CREATE TABLE `order_number` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_name` varchar(25

随机推荐