当前位置:首页 > 问答 > 正文

Redis里头过期数据怎么更快清理,时间策略那些事儿聊聊

主要参考自 Redis 官方文档关于过期机制的描述,以及一些技术社区如 Stack Overflow、Redis 中国用户组中关于性能调优的讨论)

咱们先打个比方,Redis 就像一个超级大的、带自动整理功能的储物柜,你往里面放东西(数据)的时候,可以给每件东西贴个“保质期”标签,时间一到,这东西就应该被扔掉,腾出空间来,但问题来了,柜子管理员(Redis 服务器)是怎么知道哪件东西过期了?又是怎么把它扔出去的呢?他总不能一刻不停地盯着所有东西的保质期吧?那样他自己啥也别干了,光盯着时钟看了。

Redis 用了两种聪明的方法结合起来干这件事,一种叫“惰性删除”,一种叫“定期删除”,这俩词儿听着有点技术感,但其实很好理解。

惰性删除:用到的时候才检查”

这个策略特别懒,但也特别高效,它的原则是:我不主动去找哪些数据过期了,等你来拿这个数据的时候,我顺便看一眼它过期没。

Redis里头过期数据怎么更快清理,时间策略那些事儿聊聊

你通过一个叫 my_key 的钥匙来找东西,管理员不会立刻把东西给你,而是先翻看一下贴在上面的保质期标签,如果发现已经过期了,他会直接告诉你:“没这个东西(返回 nil)”,然后顺手就把这件过期的东西扔进垃圾桶了,如果还没过期,他才把东西完好无损地交给你。

这种方法的好处是超级省力气,只会在真正有访问请求的时候才干活,对 CPU 资源几乎没有额外的浪费,那些永远没人再访问的过期数据,虽然还占着柜子,但反正也没人用,暂时放着似乎也没啥大问题。

但它的坏处也很明显:会造成空间浪费,如果柜子里有很多过期了但再也没人来问起的东西,它们就会一直霸占着空间,新的东西可能就放不进去了,这就好比冰箱里塞满了过期的食物,你因为不去翻找它们,它们就永远在那里发霉,导致你没法放新买的食物进去。

定期删除:定期主动扫一圈”

Redis里头过期数据怎么更快清理,时间策略那些事儿聊聊

为了解决“惰性删除”可能带来的空间浪费问题,Redis 又安排了第二个管理员,他就不那么懒了,他的工作是:每隔一段时间,就主动把储物柜的某些抽屉拉开,抽查一部分东西,看看有没有过期的,有的话就扔掉。

这个过程是周期性的,Redis 核心源码中(如 redis.c 中的 databasesCron 函数)定义了这种行为,这个“定期”任务并不是扫描整个数据库,那样的话如果数据量巨大,一次扫描就能让服务器卡好半天,它是这样做的:

  1. 规定每次检查的时间上限:比如每次最多只花 25 毫秒来做这件事,到点就停,防止影响正常服务。
  2. 分批抽查:从所有的钥匙(key)中随机抽取一定数量的样本进行检查和删除。
  3. 自适应速度:它会根据上一轮抽查中发现过期键的比例,来调整下一轮抽查的力度,如果上一轮发现过期键很多,说明柜子里垃圾不少,那下一轮就多抽查一些;如果发现过期键很少,说明柜子挺干净,下一轮就少抽查点,省点力气。

这种策略的目标是:在“避免占用太多 CPU 时间”和“及时释放过期内存”之间找到一个平衡点,它像一个勤劳的保洁阿姨,不会不停地打扫(那会吵到客人),也不会永远不打扫(那环境会变差),而是有节奏地、智能地进行清理。

怎么才能让清理更快、更及时呢?

Redis里头过期数据怎么更快清理,时间策略那些事儿聊聊

Redis 已经把这两种策略结合得很好了,你很难去直接“加速”它的内部清理过程,你可以通过一些配置和使用上的技巧,来让整个系统保持流畅,避免出现内存突然不够用(内存压力)的情况。

  1. 别把过期时间设得太集中:这是最最重要的一点,如果你在某一时刻,比如午夜零点,通过程序批量设置了上百万个 key,并且它们的过期时间都是第二天午夜零点,在第二天零点附近,定期删除任务会瞬间遇到海量的过期键,清理压力会非常大,这些键同时失效,可能会瞬间释放大量内存,对 Redis 主线程也可能造成一点影响(虽然删除本身是异步的),最好的办法是,给批量数据的过期时间加一个随机数,让它们的过期时间均匀分布在一段时间内,比如分散在几个小时里,这样,清理工作就能平摊开,服务器会更平稳。

  2. 关注 hz 配置参数:在 Redis 的配置文件 redis.conf 里,有个参数叫 hz,默认是 10,它表示服务器每秒执行定期任务的频率(每秒10次),这个定期任务就包括了我们上面说的“定期删除”过期键,理论上,你把它调大(比如调到 20),那么定期删除的任务执行得更频繁,过期键能被更及时地发现和清理,但代价是,CPU 的使用率会稍微高一点点,对于绝大多数情况,默认值 10 是完全足够的,只有在你的应用会产生巨量过期键,并且对内存释放的及时性要求极高时,才需要考虑谨慎地调高这个值,调之前最好在测试环境验证一下对 CPU 的影响。

  3. 别让内存真的满了:Redis 有一个叫 maxmemory-policy 的配置,是当内存用完时的应对策略,即使有过期键,但如果惰性删除和定期删除没来得及清理,内存还是可能被占满,这时候,这个策略就会生效,比如你把它设为 allkeys-lru,那么当新数据要进来但没空间时,Redis 会尝试淘汰掉最近最少使用的键(不管它过没过期),来腾出空间,设置一个合适的淘汰策略,是防止服务因内存不足而崩溃的最后一道防线,它能保证系统的可用性,从另一个角度“加速”了空间的释放。

Redis 清理过期数据靠的是“懒人抽查”(惰性删除)和“保洁巡检”(定期删除)的双重保障,你想让它工作得更顺畅,关键不在于去改动它的核心机制,而在于你如何使用它:避免大批量数据同时过期,根据实际情况微调检查频率,并且一定要设置好内存满时的淘汰规则,这样,你的 Redis 储物柜就能既干净又高效了。