点击关注公众号,利用碎片时间学习

1.过期key的删除策略

  • 定时删除:当为key设置过期时间的时候,设置一个定时任务,到时间后即刻调用并删除

  • 定期删除:每隔一定的时间,对某些key进行扫描,并删除掉其中已经过期的key

  • 惰性删除:不进行任何操作,只有访问到当前key时,如果已经过期再去删除该key

定时删除策略对于内存来说是最友好的,过期的key立刻被删除,不会过多的占用内存,但是会消耗大部分的时间片,对cpu很不友好。

惰性删除平时不做任何操作,只有key被访问到的时候才会进行判断与删除,对于cpu非常友好,但是这些已经过期的key会占用大量的内存,造成极大的浪费。

定期删除是每隔一段时间进行一次扫描,这是一种折衷的方案,既不会对cpu造成很高的负担,也不会让大量过期的key未被删除而造成浪费,但是对于扫描的频率和时间间隔设置较为复杂,如果设置的不科学,也会产生与其他方式类似的问题。

redis使用了后面两种方式来进行过期key的删除,在内存使用和cpu使用率上做了一定的取舍。与过期key删除策略经常弄混的是redis的内存逐出策略,redis的逐出策略只有在内存容量到达上线maxmemory之后才会使用,平时状态是不会使用的;而redis的过期key删除策略是每次循环都会使用的。

redis的redis的内存逐出策略有八种算法:

  • volatile-lru:设置了过期时间的key使用LRU算法淘汰;

  • allkeys-lru:所有key使用LRU算法淘汰;

  • volatile-lfu:设置了过期时间的key使用LFU算法淘汰;

  • allkeys-lfu:所有key使用LFU算法淘汰;

  • volatile-random:设置了过期时间的key使用随机淘汰;

  • allkeys-random:所有key使用随机淘汰;

  • volatile-ttl:设置了过期时间的key根据过期时间淘汰,越早过期越早淘汰;

  • noeviction:默认策略,当内存达到设置的最大值时,所有申请内存的操作都会报错,只读命令可以正常执行;

2.redis过期key删除详解

2.1 reids中的字典

redis作为一种k-v数据库,支持多种数据类型,这些数据的保存都和一个结构体redisDB相关。

在redis中,有一个链表用来做数据上的逻辑区分,链表上的每个节点都是一个redisDb,编号从0到n(可以在配置文件中修改,默认为16,通过select命令可以切换编号,编号对应结构体中的id字段)。

typedef struct redisDb {dict *dict;                 /* DB的键空间,保存了所有的key */dict *expires;              /* 保存了所有的过期key,dict的子集 */dict *blocking_keys;        /* 实现阻塞客户端使用该字典 */dict *ready_keys;           /*  唤醒阻塞客户端使用该字典 */dict *watched_keys;         /* 实现简单事物使用该字典 */int id;                     /* 数据库ID */long long avg_ttl;          /* 平均过期时间 */
}

每次对数据库进行写命令操作时,就会在字典dict中进行相应的操作(增加或者修改),如果对该key的过期时间有操作,则去expires字典中进行操作(增加或者修改)。

字典的实现主要是由下面的结构体来实现,与本文有关的两个结构是ht[2]rehashidxht[2]里面含有两个哈希表,哈希表1用来进行节点的存储,哈希表2通常情况下是空指针,没有被真实分配空间;当哈希表1的负载因子达到了扩容的要求时,则对哈希表2进行内存分配,用于进行哈希表扩容。

由于redis主线程是单线程的reactor模型,为了防止rehash造成线程阻塞,所以redis采用了渐进式rehash来进行哈希表的扩缩容,每次对一定数量的槽位进行rehash,并将下一次需要进行rehash的槽位索引位置保存在rehashidx中,方便下一次进行rehash

// 字典结构体
typedef struct dict {dictType *type;  /* 保存了一些功能函数 */void *privdata;  /* 保存一些数据用于修改字典 */dictht ht[2];    /* 哈希表 */long rehashidx; /* rehash进行到的索引位置 */  unsigned long iterators; /* 当前迭代器的运行数量 */
}

redis中的哈希表的实现方式可以归结为数组加链表,使用拉链法(链地址法)来解决哈希地址冲突;哈希表的槽位(哈希桶)个数为2^n个,每次扩缩容的结果都是2的幂次。

进行key操作的时候,首先计算放入的索引位置,idx=hash(key)%sizemark,由于掩码的值为 2^n-1,所以这里的取模运算可以简化为按位与,提高运算速度。

typedef struct dictht {dictEntry **table; // 哈希槽数组unsigned long size; // 哈希槽的个数(数组长度)unsigned long sizemask; // 哈希掩码,size-1unsigned long used; // 哈希表实际所拥有的元素个数
} dictht;

哈希表里有一个重要的指标:负载因子,可以通过used/size来计算。当负载超过一定限制的时候,需要进行扩容。与java不同的是,redis的哈希表在某个槽位过多的时候,并不会转化为红黑树;同时在负载因子过高的时候,进行渐进式的rehash。

  • 当没有bgsave或者bgrewriteaof的时候,负载因子大于1.0时

  • 当进行bgsave或者bgrewriteaof的时候,负载因子大于5.0时

  • 当为ht[1]预分配空间后,内存超过了maxmemory且负载因子大于1.618时(黄金比例0.618的倒数,此功能在redis6.2版本加入)

redis从4.0之后开始使用siphash算法来进行运算,相比之前相比(murmurhash)提高了运行速度,并且降低了哈希碰撞的概率,可以有效防止hash-dos。一个良好的hash算法能够将key分散的比较均匀,在负载因子较小的情况下,可以保证每个槽位上的元素个数都是比较少的。hash表中每个槽位上的元素个数符合Poisson分布:

当负载因子为0.5时,每个槽位上的元素个数概率分布如下所示:

P(x=k) = exp(-0.5) * pow(0.5, k) / factorial(k)
*0     0.606530659713
*1     0.303265329856
*2     0.0758163324641
*3     0.0126360554107
*4     0.00157950692633
*5     0.000157950692633
*6     1.31625577195e-05
*7     9.40182694247e-07
*8     5.87614183904e-08

当元素个数多于8个时,概率小于千万分之一

当负载因子为1.0时,每个槽位上当元素个数如下所示:

P(x=k) = exp(-1) * pow(1, k) / factorial(k)
*0     0.367879441171
*1     0.367879441171
*2     0.183939720586
*3     0.0613132401952
*4     0.0153283100488
*5     0.00306566200976
*6     0.000510943668294
*7     7.29919526134e-05
*8     9.12399407667e-06

当元素个数多于8个时,概率约为十万分之一

在正常情况下,每个哈希槽位上的节点一般都很少,可以认为在常数级时间复杂度O(1) 获取到元素。

2.2 redis的内存删除逻辑

2.2.1 redis的事件驱动模型

redis是一个单线程事件驱动的模型,主线程主要在aeMain中进行,通过配置文件中的属性hz来确定每秒钟含有几个循环周期,默认为10。

redis在初始化和加载后,就在aeMain中进行循环,直到接收到信号或发生错误才会退出,每个周期都会按照如下步骤进行处理:

  1. 处理beforesleep回调函数相关功能

  2. 处理时间事件

  3. 处理aftersleep回调函数相关功能

  4. 处理文件事件

  5. 进入下一次循环(回到a)

void aeMain(aeEventLoop *eventLoop) {eventLoop->stop = 0;while (!eventLoop->stop) {if (eventLoop->beforesleep != NULL)eventLoop->beforesleep(eventLoop);aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);}
}
  • beforesleep回调函数主要功能有进行一次快速的过期key淘汰,发送命令要求slave上报backlog偏移量,尝试释放blocking的客户端,aof文件刷盘等

  • 时间事件主要放在一个无序链表里,正常运行的redis只有一个事件事件ServerCron(redis启动加载数据时,有两个时间事件),发起一次慢速的过期key淘汰,尝试进行rehash,发送定时心跳,检测部分后台bio线程的状态等

  • aftersleep回调函数目前只有module相关功能的处理

  • 文件事件主要是通过io多路复用功能,获取就绪的文件事件进行相应的读写处理

2.2.2 redis过期key删除的实现

这里使用了pyhton的伪代码代替了原始的c代码做说明,主要承接上文介绍了两种清理模式(快速清理和慢速清理),这个函数是过期key淘汰功能的核心,可以动态的调节cpu用在扫描过期键的时间,当过期键较少时,使用更少的cpu时间片;当过期key较多时,则会表现的比较激进。

beforesleep回调函数调用时,会尝试一次快速清理功能,这种清理最大能持续EXPIRE_FAST_CYCLE_DURATION时间,并且在相同的时间内不会重复调用;

aftersleep回调函数阶段,会调用慢速清理功能,最大能调用每个redis处理周期ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC百分比的时间片。

通常情况下慢速淘汰功能能够淘汰大量的key,只有频繁触发时间限制的时候,说明慢速清理还剩下很多过期的key没有清理,需要穿插一些快速清理功能来尽可能的删除过期key。

ACTIVE_EXPIRE_CYCLE_SLOW = 0  # type标志位,持续时间比较久的慢速清理模式
ACTIVE_EXPIRE_CYCLE_FAST = 1  # type标志位,持续时间比短的快速清理模式ACTIVE_EXPIRE_CYCLE_FAST_DURATION = 1000  # 快速清理模式的最长持续时间,单位:微秒
ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP = 20  # 每次扫描随机抽取的key个数
ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC = 25  # 扫描key所占用的cpu最长时间片
CRON_DBS_PER_CALL = 16  # 每次随机扫描的db个数dbnums = 0  # server里面设置了多少个redisDb的逻辑分区
current_db = 0  # 当前遍历到的数据库编号
timelimit_exit = 0  # flag标识位,用于判断是否是到达了时间上限才退出循环
last_fast_cycle = 0  # 上一次快速淘汰开始的时间def activeExpireCycle(type):# 获取现在的运行时间,用于信息push和计算是否需要退出的baselinestart = time.time()# 设定每次需要遍历的数据库dbs_per_call = CRON_DBS_PER_CALLif type == ACTIVE_EXPIRE_CYCLE_FAST:# 如果上一次扫描没有触发时间限制就退出了,说明过期key数量不多,不要浪费性能在扫描key上if not timelimit_exit:return# 如果距离上次快速淘汰开始的时间间隔小于2倍的快速淘汰持续时间,直接返回if start < last_fast_cycle + ACTIVE_EXPIRE_CYCLE_FAST_DURATION * 2:return# 设定本次快速淘汰开始的时间,为下一次调用做时间baselinelast_fast_cycle = start# 如果每次扫描的数据库数量大于实际系统分配的逻辑分区数量,按照实际逻辑分区数量进行扫描# 如果上次扫描触发了时间限制,说明过期key比较多,需要进行全部逻辑分区的扫描if dbs_per_call > server.dbnum or timelimit_exit:dbs_per_call = dbnums# 计算本次循环最多能持续的时间,1000000微秒*(25/100)/ 10# 1秒钟10hz,每次redis的处理周期为100毫秒(100000微秒),25%cpu时间分片就是25000微秒timelimit = 1000000 * ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC / server.hz / 100# 如果是快速清理模式,则每次最多能持续1000微秒if type == ACTIVE_EXPIRE_CYCLE_FAST:timelimit = ACTIVE_EXPIRE_CYCLE_FAST_DURATION# 遍历指定数量(dbs_per_call)的数据库for i in range(0, dbs_per_call):# 如果触发了时间限制,直接退出循环if time.time() - start > timelimit:break# 当前数据库中不存在过期的key,直接扫描下一个数据库if redisDb[i].expires.size() == 0:continue# 如果当前数据库含有过期时间的key数量小于1%,直接扫描下一个数据库if redisDb[i].expires.size() * 100.0 / redisDb[i].dict.size() < 1:continuewhile True:# 获取每次扫描的key的数量lookup_nums = min(redisDb[i].expires.size(), ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)# 已经过期的key的数量expired = 0while lookup_nums > 0:# 随机获取一个key判断是否过期lookup_key = random_get_key_from_expire(redisDb[i])# 如果过期了,就删除if is_expire(lookup_key):del (lookup_key)expired += 1# 判断当前时刻是否触发了时间限制,如果触发了限制,直接退出if time.time() - start > timelimit:breakbreak  # 两个break,不符合python语法,这里表示结束全部的循环# 如果本次循环扫描出来的key已经过期的比例小于25%,则去扫描下一个数据库# 否则说明本数据库过期的key较多,立刻在本数据库中再次扫描和清理if expired * 4 / lookup_nums < 1:break
2.2.3 redis过期key优化点

上文中提到了,过期key是随机从全部的含有过期时间的key中进行选择。使用的算法如下,这里存在的不足是,当过期key数量较少时,哈希桶槽位上大部分的元素为空,随机数生成后所得到的哈希桶槽位经常miss,需要再次进行随机,会消耗过多的时间片在生成随机数上,而不是清理过期key。

def random_get_key_from_expire(redisDb):slot_nums = 0# 如果在进行rehash,则两个哈希表都可能有key存在,需要从中随机选择一个if (is_rehash(redisDb.expires)):slot_nums = ht[0].size + ht[1].sizeelse:slot_nums = ht[0].sizeheadEntry = Nonewhile True:# 从所有的槽位中随机选择一个slot_idx = random.randint(0, slot_nums)# 直到找到一个槽位不为空的哈希桶,就停止循环if (slot_idx > ht[0].size):# 如果所得到的索引大于ht[0]的大小,说明落在了ht[1]中tempEntry = ht[1].get_index(slot_idx - ht[0].size)if tempEntry != None:headEntry = tempEntrybreakelse:tempEntry = ht[0].get_index(slot_idx)if tempEntry != None:headEntry = tempEntrybreakcurrEntry = headEntrylen = 0# 遍历链表获得长度while currEntry.next:currEntry = currEntry.nextlen += 1# 从链表中随机选择一个节点返回listele = random.randint(0, len)while listele:headEntry = headEntry.nextlistele -= 1return headEntry

此外,如果redis的写入量比较大,且key过期时间比较短,即使两种淘汰方式同时进行,也会超过redis的处理能力,依然会造成数据的堆积,如果容量继续上涨超过,就会进行内存淘汰(使用allkeys-lru淘汰策略,会优先删除lru较小的key,由于惰性删除的原因,已过期的key的lru会通常情况下比较小,会被优先逐出)。

由于走了另外一套逻辑(逐出逻辑)造成了cpu时间片的浪费,它们最终的结果都是key删除,但是却进行了不同的筛选策略。实际使用场景中遇到这种情况的时刻不多,对于性能和吞吐量的影响不大,但是也是一个可以优化的点。

实际使用中,还有一种人为触发过期key淘汰的方法,就是通过scan命令来进行全库的扫描,通过控制scan命令的游标和count数量,可以预期的控制淘汰的激烈程度,不会对主线程造成阻塞。

redis在进行key的删除的时候,如果开启了异步删除,则当被删除的key所对应的val占用空间大于64字节时,会将这个key标记为删除后直接返回+OK,然后将val放到后台的bio线程里面进行删除,防止阻塞主线程;如果占用的空间小于64字节,即使开启了异步删除,在最后运行的时候也会同步的进行删除(redis优秀的性能优化在细节之末随处可见,针对很多场景都做了优化,并抽象出参数给用户动态配置,它的高性能是与redis作者精益求精的修改分不开的)。

2.2.4 redis6对过期key删除的优化

redis6针对以上的问题提出了几个改进点,首先将原来的随机抽样检查过期key变成了按照哈希桶顺序遍历检查过期key。通过在redisDb结构体中增加long expires_cursor字段,用来记录上一次遍历到的游标位置,每次遍历都会取到这个游标位置对应的索引,然后继续下去,使得cpu的时间片更多的用来进行key过期的判断和删除上。

typedef struct redisDb {dict *dict;                 dict *expires;              dict *blocking_keys;        dict *ready_keys;           dict *watched_keys;         int id;                     long long avg_ttl;          unsigned long expires_cursor; /* 主动过期循环的游标*/list *defrag_later;         /* key组成的链表,用来进行内存碎片整理 */
} redisDb;

另外在server的配置中增加了activate_expire_effort标识位,可以被设定为1-10,用来表示定期删除淘汰key的激烈程度。在系统设定好activate_expire_effort之后,redis的定期删除循环逻辑每次都会重新计算阈值,比如快速淘汰循环最大的持续时间,慢速淘汰循环最大的持续时间,这些参数在之前的淘汰算法中都是一个确定的值。通过抽象成一个配置文件的参数,可以通过命令热加载动态的调节,达到redis吞吐量和容量使用的“均衡”。

struct redisServer {...int active_expire_enabled;      /* Can be disabled for testing purposes. */int active_expire_effort;       /* From 1 (default) to 10, active effort. */...
}
ACTIVE_EXPIRE_CYCLE_SLOW = 0  # type标志位,持续时间比较久的慢速清理模式
ACTIVE_EXPIRE_CYCLE_FAST = 1  # type标志位,持续时间比短的快速清理模式ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP = 20  # 每次每个db扫描的key个数
ACTIVE_EXPIRE_CYCLE_FAST_DURATION = 1000  # 快速清理模式的最长持续时间,这里只是一个基线
ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC = 25  # 扫描key所占用的cpu最长时间片
ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE = 10  # 开启efforts后,过期key占据的百分比
CRON_DBS_PER_CALL = 16  # 每次随机扫描的db个数current_db = 0  # 当前遍历到的数据库编号
timelimit_exit = 0  # flag标识位,用于判断是否是到达了时间上限才退出循环
last_fast_cycle = 0  # 上一次快速淘汰开始的时间def activeExpireCycle(type):# 一个统计量,表示系统对于过期key扫描的激进程度(0~9)# 该配置可以通过配置文件来进行设置,根据集群的平均过期时间,动态的设定effort = server.active_expire_effort - 1,# 每次循环扫描的key的数量config_keys_per_loop = ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP + ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP / 4 * effort# 快速清理循环的持续时间config_cycle_fast_duration = ACTIVE_EXPIRE_CYCLE_FAST_DURATION + ACTIVE_EXPIRE_CYCLE_FAST_DURATION / 4 * effort# 清理循环周期所占用的最大cpu时间片百分比config_cycle_slow_time_perc = ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC + 2 * effort# 有过期时间的key所全部key占的最多的百分比config_cycle_acceptable_stale = ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE - effort# 全局变量,函数退出后值仍然保存,留待下一次函数调用global last_fast_cycleglobal current_dbglobal timelimit_exitstart = time.time()dbs_per_call = CRON_DBS_PER_CALLiteration = 0if type == ACTIVE_EXPIRE_CYCLE_FAST:# 如果上一次扫描没有触发时间限制就退出,并且系统所含有的过期key的比例小于配置的阈值直接返回# 因为此时过期快key的数量不多,并不需要浪费过多的cpu时间片在扫描过期key上if (not timelimit_exit) and (server.stat_expired_stale_perc < config_cycle_acceptable_stale):return# 如果距离上次快速周期循环开始的时间间隔小于2倍的快速周期循环持续时间,直接返回if start < last_fast_cycle + config_cycle_fast_duration * 2:return# 设定本次快速淘汰开始的时间,为下一次调用做时间baselinelast_fast_cycle = start# 如果每次扫描的数据库数量大于实际系统分配的逻辑分区数量,按照实际逻辑分区数量进行扫描# 如果上次扫描触发了时间限制,说明过期key比较多,需要进行全部逻辑分区的扫描if dbs_per_call > server.dbnum or timelimit_exit:dbs_per_call = server.dbnums# 计算本次循环最多能持续的时间,1000000微秒*(25/100)/ 10# 1秒钟10hz,每次redis的处理周期为100毫秒(100000微秒),25%cpu时间分片就是25000微秒# 如果设置了effort,则会增加用于过期key删除的cpu时间片,例如effort=6,那么将有35000微秒用于处理过期keytimelimit = 1000000 * config_cycle_slow_time_perc / server.hz / 100# 如果是快速清理模式,则每次最多能持续config_cycle_fast_duration微秒if type == ACTIVE_EXPIRE_CYCLE_FAST:timelimit = config_cycle_fast_duration# 遍历指定数量(dbs_per_call)的数据库for i in range(0, dbs_per_call):# 如果触发了时间限制,直接退出循环if time.time() - start > timelimit:break# 获取每次要扫描的数据库编号,并记录下来,这样可以让cpu将时间片公平的分给每个数据库db_idx = current_db % server.dbnum# 先变更下一次要扫描的数据库,方式程序因超时返回时忘记变更需要扫描的数据库,# 也是为了将时间片公平的分散到每个数据库db_idx += 1while True:# 当前数据库中不存在过期的key,直接扫描下一个数据库if redisDb[db_idx].expires.size() == 0:break# 如果当前数据库含有过期时间的key数量小于1%,直接扫描下一个数据库if redisDb[db_idx].expires.size() * 100.0 / redisDb[db_idx].dict.size() < 1:break# 获取每次扫描的key的数量num = redisDb[db_idx].expires.size()if num > config_keys_per_loop:num = config_keys_per_loop  # 每次至多只会选择config_keys_per_loop个key进行查询# 最多遍历的哈希桶数量max_buckets = num * 20# 已经遍历的哈希桶数量checked_buckets = 0# 已经扫描的过期key的个数sampled = 0# 已经过期key的个数expires = 0# 如果扫描的哈希桶数量过多或者已经扫描了规定数量的key,就退出循环防止占用过多时间while sampled < num and checked_buckets < max_buckets:# 字典里只有两个哈希表ht[0],ht[1],每次循环会选择其中的一个for table in (0, 1):# 没有进行rehash时,ht[1]为空,遍历到此处时直接返回if table == 1 and is_rehash(redisDb[db_idx].expires):break# 保存了上一次遍历到的哈希桶索引位置idx = redisDb[db_idx].expires_cursor# 与掩码进行计算,确定本次开始遍历的哈希桶索引位置idx = idx & redisDb[db_idx].expires.ht[i].sizemark# 获取哈希桶索引位置对应的链表的头节点dictEntry = redisDb[db_idx].expires.ht[i].bucket[idx]while dictEntry:# 遍历链表,如果过期了就删除,并计数已经过期的keyif is_expire(dictEntry):del (dictEntry)expires += 1dictEntry = dictEntry.nextsampled += 1# 记录下一次要遍历的哈希桶的索引位置redisDb[db_idx].expires_cursor += 1# 如果扫描时间过长达到了规定的阈值,则直接返回if time.time() - start > timelimit:return  # 直接返回全部的# 如果过期的key占比高于阈值,说明当前库里面的过期key很多,需要再次遍历进行淘汰if expires * 100.0 / sampled > config_cycle_acceptable_stale:continue# 如果扫描到的所有的桶都是空的,触发了max_buckets限制而退出,说明没有清理到过期的key# 过期的key都在其他的桶之中,需要进行再次的扫描elif sampled == 0:continueelse:break

来源:blog.csdn.net/qq_38856118/article/

details/115796879

推荐:

主流Java进阶技术(学习资料分享)

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!

Redis 过期 key 删除,那些不得不说的事情!相关推荐

  1. 2022-04-12 redis过期key删除策略

    1 定时删除: 在设置键过期时间的同时,创建一个定时器,让定时器在过期时间来临时,立即执行对键的删除操作; 这样做有一个弊端,就是当有很多key同一时间过期的时候,会造成一时间的CPU紧张,从而可能影 ...

  2. Redis源码分析:过期key删除与设置key的过期时间

    Redis中设置key过期时间与过期key的处理流程 在Redis中,可以再设置值的时候就设置该Key的过期时间,也可以通过在expire命令来设置某个key值的过期时间,并且在了解完设置过期时间之后 ...

  3. Redis 过期 key 清理删除策略汇总

    Redis 底层使用了三种策略来清理过期的key 一.被动清理 触发条件:读/写一个已经过期的key 当读写一个key时,Redis首先检查key是否存在,若存在且已经过期,则删除key的同时返回ni ...

  4. redis过期key的清理/删除策略

    一,有三种不同的删除策略 立即清理.在设置键的过期时间时,创建一个回调事件,当过期时间达到时,由时间处理器自动执行键的删除操作. 惰性清理.键过期了就过期了,不管.当读/写一个已经过期的key时,会触 ...

  5. redis过期key的删除策略

    前言 在使用redis的过程中,不免会产生过期的key,而这些key过期后并不会实时地马上被删除,当这些key数量累积越来越多,就会占用很多内存,因此在redis底层同时使用了三种策略来删除这些key ...

  6. Redis 过期键删除策略

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:八个开源的 Spring Boot 学习资源,你值得拥有个人原创+1博客:点击前往,查看更多 作者:罗曼蒂克 链 ...

  7. Redis 过期键删除策略、内存淘汰机制

    文章目录 过期键删除策略 定时删除 惰性删除 定期删除 Redis的选择 内存淘汰机制 redis中缓存的数据是有过期时间的,当缓存数据失效时,redis会删除过期数据以节省内存,那redis是怎样怎 ...

  8. redis过期key监听事件

    文章目录 redis安装 docker拉取 启动 redis 配置 命令监听 问题 程序监听 具体监听类 效果 总结 redis常用语缓存操作,但是redis功能不仅仅于此.今天我们来看看redis的 ...

  9. Redis过期key是怎么样清理的?

    在Redis中,对于过期key的清理主要有惰性清除,定时清理,内存不够时清理三种方法,下面我们就来具体看看这三种清理方法. (1)惰性清除 在访问key时,如果发现key已经过期,那么会将key删除. ...

最新文章

  1. 基于C++的骨架提取的鼻祖算法
  2. Device Tree(一):背景介绍
  3. Redis Lua脚本实现原子性操作
  4. 商品品牌信息的增删改查操作步骤_javaweb09-Servlet增删改查
  5. mysql执行脚本的方法
  6. 无人驾驶飞机来了!空难后波音的电动飞机你敢乘吗?
  7. foxmail邮件加载失败重试_Foxmail提示错误的解决方案
  8. Java基础---集合框架---迭代器、ListIterator、Vector中枚举、LinkedList、ArrayList、HashSet、TreeSet、二叉树、Comparator
  9. 【机器人学】机器人运动学基础
  10. [demo] 微信小程序Demo:树芽读书(一个不错的书籍朗读小程序)
  11. Duet Display用一根数据线将iPad变身扩展显示器
  12. iOS篇—plist文件
  13. MySQL查询不同年份母亲节_不同国家的母亲节发展故事
  14. matlab求解杜分方程,[转载]时间序列分析matlab简单函数
  15. windows局域网能ping通别人,别人确无法ping通自己的解决
  16. Android基础——从存储介质打开文档
  17. 斯坦福大学教授,极力推荐5本python入门书籍,入门最快基础最好
  18. 2018年五一小长假全国旅游大数据报告出炉,这些景点太火爆!
  19. 利用phantomjs模拟登录网站(帐号登陆)
  20. uni-app之- 后台唤醒app时,跳转对应锁屏页面

热门文章

  1. 广元云计算大数据中心一期项目10月底投用
  2. MIPI DSI TO HDMI 1.4的桥接芯片 支持HDCP
  3. Python基本用法(一)
  4. 选课系统java源文件_学生选课系统 - WEB源码|JSP源码/Java|源代码 - 源码中国
  5. 除了Figma,再给你介绍10款好用的协同设计软件
  6. HHUOJ 1852 窃贼
  7. 张驰咨询:中国企业如何实施精益生产管理?
  8. Realtek HD声卡前面板无输出解决办法
  9. 博客园离线写博客工具
  10. 免费天气预报代码 滚动天气预报代码 大全