Redis针对过期key的删除策略

Redis使用三种策略处理过期key:定时删除、惰性删除和定期删除。定时删除在key过期时立即删除,最节省内存但可能消耗较多CPU时间。惰性删除在获取key时检查并删除过期key,最节省CPU时间但可能导致内存浪费。定期删除则是两者折中,按固定周期检查并删除过期key,防止过度消耗CPU和内存。Redis实际采用惰性和定期删除策略结合,平衡资源使用。

Redis数据库键的过期时间都保存在过期字典中,可使用exit key 或ttl key 或者pttl key命令判断key是否过期。若内存中存放的key-value对较多,Redis应该如何才能保证不影响查询效率又能控制其中的有效缓存?(尽可能保证Redis中存放的key是未过期的key)。
这个问题有三种可能的答案,分别代表了三种不同的过期删除策略:

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

1.定时删除:

定时删除策略对内存是最友好的:通过使用定时器,定时器删除策略可以保证过期key会尽可能快的被删除,并释放过期key所占用的内存。
另一方面定时删除策略缺点是,它对CPU时间是最不友好的:在过期key较多的情况下,删除过期key这一行为可能会占用相当一部分CPU时间,在内存不紧张但是CPU时间非常紧张的情况下,将CPU时间用在删除和当前任务无关的过期key上,无疑会对服务器的响应时间和吞吐量造成影响。

2.惰性删除:

惰性删除策略对CPU时间来说是最友好的:程序只会在取出key时才对key进行过期检查,这里可以保证删除过期key的操作只会在非做不可的情况下进行,并且删除的目标仅限于当前处理的key,这个策略不会在删除其他无关的过期key上花费任何CPU时间。
惰性删除策略的缺点是:它对内存是最不友好的:如果一个key已经过期,而这个key有仍然保留在数据库中,那么只要这个过期key不被删除,它所占用的内存就不会释放。
在使用惰性删除策略时,如果数据库中有非常多的过期key,而这些过期key又恰好没有被访问到的话,那么它们也许永远也不会被删除(除非用户手动执行FLUSHDB),我们甚至可以将这种情况看作是一种内存泄露——无用的垃圾数据占用大量内存,而服务器不能自己去释放他们,对于运行状态非常依赖内存的Redis服务器来说肯定不是一个好消息。
2.1惰性删除策略的实现:
过期key的惰性删除策略由db.c/expireIfNeeded函数实现,所有读写该数据库的Redis命令在执行前都会调用expireIfNeeded函数对输入key进行检查:

  • 如果输入key已经过期,那么expireIfNeeded函数将输入key从数据库中删除;
  • 如果输入key未过期那么expireIfNeeded函数不做任何操作。
    调用expireIfNeeded函数过程如下:
    这里写图片描述
    expireIfNeeded函数就像一个过滤器,它可以在命令真正执行前,过滤掉过期的输入key,从而避免命令接触到过期key。另外因为每个被访问的key都可能因为过期而被expireIfNeeded函数删除,所以每个命令的实现函数都必须能同时处理key存在以及不存在的情况:
  • 当key存在时:命令按照key存在的情况执行;
  • 当key不存在或者因为过期而被expireIfNeeded函数删除时,命令按照key不存在的情况执行。

3.定期删除:

定期删除策略是定时删除策略和惰性删除策略的一个折中,既兼顾了内存占用又兼顾CPU时间,但是该策略的难点是确定删除操作执行的市场和频率:
1)如果删除操作执行的太频繁,或者执行时间太长,定期删除策略就会退化成定时删除策略,以至于将CPU时间过多的消耗在删除过期key上面;
2)如果删除操作执行的太少,或者执行的时间太短,定期删除策略又会和惰性删除策略一样,出现浪费内存的情况。
3.1定期删除策略的实现:
定期删除策略的实现由expire.c/activeExpireCycle函数实现。每当Redis的服务器周期性执行server.c/activeExpireCycle函数时,activeExpireCycle函数就会被调用,它在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分key的过期时间,并删除其中的过期key。整个过程可用伪代码描述如下:

#默认每次检查的数据库数量
DEFAULT_DB_NUMBERS = 16
#默认每个数据库检查的键数量
DEFAULT_KEY_NUMBERS = 20
#全局变量,记录检查进度
current_db = 0
def activeExpireCycle():
#初始化要检查的数据库数量
#如果服务榕的数据库数量比DEFAULT DB NUMBERS 要小
#那么以服务器的数据库数量为准
if server.dbnum < DEFAULT_DB_NUMBERS :
db_numbers = server.dbnum
else :
db_numbers = DEFAULT_DB_NUMBERS
#遍历各个数据库
for i in range(db_numbers) :
#如果current_db 的值等于服务榕的数据库数量
#这表示检查程序已经遍历了服务榕的所有数据库一次
#将current_db重置为0 ,开始新的一轮遍历
if current_db == server.dbnum:
current_db = 0
#获取当前要处理的数据库
redisDB = server.db[current db)
#将数据库索引增1 ,指向下一个要处理的数据库
current_db += 1
#检查数据库键
for j in range(DEFAULT_KEY_NUMBERS):
#如果数据库中没有一个键带有过期时间,那么跳过这个数据库
if redisDB.expires.size () == 0: break
# 随机获取一个带有过期时间的键
key with_ttl = redisDb.expires.get_random_key ()
#检查键是否过期,如果过期就删除它
if is_expired (key with ttl):
delete_key (key_with_ttl )
#已达到时间上限,停止处理
if reach_time_limit(): return

activeExpireCycle函数工作模式可总结如下:

  • 函数每次运行时,都从一定数量的数据库中取出一定数量的随机key进行检查并删除其中的过期key;
  • 全局变量current_db会记录当前activeExpireCycle函数检查的进度,并在下一次activeExpireCycle被调用时接着上一次的进度进行处理。
  • 随着activeExpireCycle函数的不断执行,服务器中的所有数据库都会被检查一遍,这时函数将current_db变量重置为0,然后再次开始新一轮的检查工作。

    在调用定期删除策略时需要传个type参数,用以区分是用“快速模式”还是“正常模式”。
    在“正常模式”下,会遍历每一个编号下的库,然后最多随机获取20个带过期时间的key(20是宏ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP的默认值),倘若key太多则直接return了。接着调用redis.c/activeExpireCycleTryExpire函数尝试去删除它,能删除成功则再发送expired的pub通知给订阅者即可。定期删除的定时时长是100ms.

在Redis服务器实际使用的是惰性删除策略和定期删除策略两种策略:通过配合使用这两种删除策略,服务器可以很好的在合理使用CPU时间和避免浪费内存空间之间取得平衡。

<think>嗯,用户这次的问题是关于Redis策略的深入探讨,涉及拒绝策略过期策略删除策略三者的区别和应用场景。从用户引用的资料来看,ta已经掌握了一些基础概念,但需要更系统的对比分析。 用户可能正在设计高并发系统,需要根据业务特点选择合适的Redis策略组合。ta提到“应用场景”,说明需要落地指导而非纯理论。有趣的是,ta特意区分了这三个易混淆的概念,说明之前可能遇到过策略配置当导致的线上问题。 从技术角度看,这三个策略确实容易混淆: - 删除策略针对的是已过期的键如何清理(后台定时 vs 惰性检查) - 过期策略是键的生命周期管理方式(立即过期 vs 惰性过期) - 拒绝策略则是内存足时的应急方案(报错 vs 淘汰数据) 用户引用的资料[1][3]提到惰性删除和定时删除,[2]提到allkeys-lru淘汰策略,[4][5]补充了Redis适用场景。需要把这些碎片信息整合成体系化的回答。 特别要注意用户上次讨论过RR隔离级别,说明ta对技术细节有钻研精神。这次回答需要保持同等专业深度,比如解释惰性删除为什么可能引发内存泄漏,或者volatile-lru在电商促销场景的应用价值。 考虑到用户连续提问数据库和缓存,可能是后端开发或架构师。回答要突出实战性,比如给出“秒杀场景推荐策略组合”这样的具体建议。同时必须澄清常见误区,比如很多人认为maxmemory-priority只对设置了TTL的键生效,其实allkeys系列策略会影响所有键。</think>以下是Redis中拒绝策略过期策略删除策略的详细对比及应用场景分析,三者共同协作保障Redis高性能与数据可靠性: --- ### **1. 删除策略过期键清理机制)** 解决核心问题:**如何清理已过期的键?** - **惰性删除(Lazy Expiration)** - **原理**:只在访问键时检查过期时间,若过期删除 - **特点**: - ✅ CPU友好(无额外开销) - ❌ 内存友好(未访问的过期键长期占用内存) - **场景**:读少写多的低频数据缓存 - **示例**: ```python # 伪代码:GET命令触发检查 def get(key): if redis_db[key].expire_time < now(): del redis_db[key] # 删除过期键 return None return redis_db[key].value ``` - **定期删除(Periodic Expiration)** - **原理**:定时任务抽样扫描过期键(默认每秒10次) - **特点**: - ⚖️ 平衡CPU与内存(通过调整扫描频率) - ⚠️ 可能漏删(非100%覆盖) - **场景**:通用场景的折中方案(Redis默认策略) - **立即删除(主动驱逐)** - **原理**:设置键过期时立即启动删除任务 - **特点**: - ✅ 内存最及时释放 - ❌ 高CPU开销(影响吞吐量) - **场景**:对内存敏感且可接受性能波动的场景 > 📌 **总结**:删除策略关注**物理删除时机**,确保过期数据最终被清理[^1][^3]。 --- ### **2. 过期策略(键生命周期管理)** 解决核心问题:**何时判定键过期?** - **TTL(Time To Live)** - **原理**:显式设置键的存活时间(如 `SET key value EX 30`) - **实现**: - Redis内部维护**过期字典**(expires dict)存储键->过期时间戳 - **特点**:精确控制数据生命周期 - **惰性过期(逻辑过期)** - **原理**:主动删除键,仅在访问时判断是否逻辑“失效” - **场景**: - 允许暂时数据一致(如缓存雪崩保护) - 结合删除策略实现物理清理 > 📌 **总结**:过期策略定义**数据有效性规则**,需依赖删除策略执行物理清理[^1][^3]。 --- ### **3. 拒绝策略(内存溢出处理)** 解决核心问题:**内存足时如何应对新写入?** 通过 `maxmemory-policy` 配置: | 策略 | 机制 | 适用场景 | |------|------|----------| | **noeviction** | 拒绝所有写入请求(默认) | 数据绝对可丢失场景 | | **allkeys-lru** | 淘汰最近最少使用的键 | 通用缓存(平衡热度) | | **volatile-lru** | 仅淘汰**有过期时间**的LRU键 | 缓存+持久数据混合存储 | | **allkeys-random** | 随机淘汰任意键 | 无访问规律的数据 | | **volatile-ttl** | 优先淘汰剩余存活时间最短的键 | 时效性敏感数据(如验证码) | | **volatile-lfu** | 淘汰使用频率最低的键 | 热点数据分布均场景 | > 📌 **关键区别**:`volatile-*` 系列**只淘汰设定了TTL的键**,而 `allkeys-*` 可能淘汰任何键[^2][^3]。 --- ### **三策略协作关系** ```mermaid graph TD A[写入数据] --> B{是否设置TTL?} B -->|是| C[过期字典记录过期时间] B -->|否| D[永久存储直至内存足] C --> E[删除策略定时/惰性清理] D --> F[内存足时触发拒绝策略] F --> G[根据maxmemory-policy淘汰数据] ``` --- ### **典型应用场景** 1. **高频访问缓存** - **组合**:`定期删除` + `allkeys-lru` + **设置TTL** - **效果**:自动淘汰冷数据,保证热数据驻留内存 2. **会话存储(Session)** - **组合**:`惰性删除` + `volatile-ttl` - **原因**:会话自然过期优先,避免误删活跃会话 3. **实时排行榜** - **组合**:`立即删除` + `noeviction` - **原因**:数据需强一致性,宁拒绝写入也淘汰 4. **消息队列(临时数据)** - **组合**:`定期删除` + `volatile-lru` - **优势**:平衡内存与CPU,自动清理消费后的数据 > ⚠️ **避坑提示**: > - 使用 `volatile-*` 策略时**必须设置TTL**,否则等同于 `noeviction` > - 混合存储场景慎用 `allkeys-lru`,可能误删持久化数据[^2][^4] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值