侧边栏壁纸
  • 累计撰写 247 篇文章
  • 累计创建 16 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Redis 过期删除策略(一):通过定期扫描主动删除过期键

kaixindeken
2021-04-28 / 0 评论 / 0 点赞 / 122 阅读 / 1,469 字

概述

Redis 底层会通过一张巨大的哈希表来维护所有不同类型的键值对,我们可以通过 Redis 为 KEYS 提供的指令来操作这些键值对:

1.jpeg

其中包括设置过期时间的 EXPIRE、EXPIREAT 指令,前者用于指定多少秒后过期,后者用于指定具体的过期时间点,无论使用哪个指令,在过期时间到来后,都无法再访问对应的键值对了。

Redis 底层主要有三种策略来删除过期的键值对:

  • 主动删除:Redis 会定期扫描设置过期时间的键进行删除;
  • 被动删除:当访问某个键值对时,先检查是否过期,如果过期则将其懒惰删除;
  • 触发内存上限后根据配置的淘汰策略进行清理。

下面我们一一来介绍这几种过期删除策略。

定期扫描

Redis 会将设置了过期时间的键放到一个独立的字典中,然后通过一个专门的异步线程定期扫描这个字典,将过期键删除,这个扫描频率默认是每秒十次,这个频率值可以通过 Redis 配置文件的 hz 配置项进行调整:

hz 10

从这个角度看,Redis 并不是纯单线程的,单线程是用于处理客户端请求的,使用独立的异步线程处理定时任务是为了避免定时任务阻塞主线程,影响 Redis 对客户端请求的处理性能。

Redis 的定时任务会记录在一个称之为小顶堆的数据结构中,在这个堆中,最快要执行的任务排在堆的最上方,在每个循环周期中,Redis 都会将小顶堆里面已经到点的任务立即进行处理。

贪心算法

需要注意的是,Redis 不会一次性遍历过期字典中的所有键,因为这会非常耗时,不符合 Redis 高性能的调性,取而代之的,Redis 采用一种简单的贪心策略:

  1. 从过期字典中随机选取 20 个键;
  2. 删除这 20 个键中已经过期的键;
  3. 如果过期的键比例超过 25%,那就重复步骤 1。

同时,为了保证过期扫描不会出现循环过度,导致线程卡死,这个算法还增加了扫描时间的上限,默认不会超过 25ms。

以上过期扫描和删除实现源码可以在 Redis 代码库 expire.c 的 activeExpireCycle 方法中看到。

避免大量键同时过期

如果 Redis 中有大量键同时过期,Redis 会循环多次扫描过期字典,直到字典中过期键变得稀疏(比例低于 25%),这一方面会导致数据库请求飙升,影响系统并发性能(如果 Redis 用作缓存的话),另一方面也会导致 Redis 主线程处理客户端请求出现卡顿,因为内存管理器需要频繁回收内存页,这会消耗 CPU 资源。

所以我们在生产环境,要尽量避免大量 Redis 键同时过期,这种场景一般在大促活动时会特别集中出现,因为很多促销活动都是定点定时的,为了避免这个问题,我们可以在设置过期时间时让其尽可能离散分布,另一方面也可以在活动前主动刷新过期时间,避免大促期间出现大量 Redis 键过期,影响系统性能。

当然,定时任务存在一个问题,就是它不是全量扫描所有过期键的,这就导致「漏网之鱼」的存在,为了保证 Redis 键过期自动清理的功能正常,就需要引入被动删除策略,即访问指定键时先判断其是否过期,如果过期则将其删除,这样,即便主动删除策略漏掉某些键了,也会在访问这些键时确保已过期的键不存在。

0

评论区