Redis 对于过期的 key 有两种清理机制,一种是主动的(active way),一种是被动的(passive way)。

被动清理机制

被动清理机制,也可以称为惰性清理机制。当有客户端尝试访问一个 key 的时,如果这个 key 已经过期,则会将这个 key 清理掉。

主动清理机制

显然,被动清理机制是存在不足的,不可能将所有过期的 key 都清理掉,尤其是其中访问频率很低的冷数据。因此,Redis 有主动的定期清理机制,定期在带有过期时间的 key 中随机测试一些 key,并将其中过期的 key 清除掉。

具体地,Redis 每秒执行 10 次以下流程:

  1. 从带有过期时间的 key 中随机抽取 20 个 key;
  2. 删除其中所有已过期的 key;
  3. 如果有超过 25% 的 key 已过期,则从第 1 步开始重新执行。

通过上述规则,我们可以有如下推论:

  • 每次抽取的样本可以代表整个 key 空间:因为每次都是从所有带有过期时间的 key 中随机抽样,所以抽取的 20 个样本中已过期的 key 的占比的样本均值的期望等于所有 key 中已过期的 key 的占比的期望。
  • 在任意时刻,Redis 中存储的已过期的 key 的占比的期望小于等于 25%:因为我们每次清理时,都将样本中已过期的 key 的占比清理到 25% 以下,所以每一次清理后,所有 key 中已过期的 key 的占比的期望小于等于 25%。

即,在任何给定时刻,存储器中已过期 key 的最大数量小于等于每秒写入操作的最大数量除以 4。

总结

通过上述机制,我们可以理解如下场景:

当我们使用 scan 和 get 命令遍历一个已经存在较长时间的数据库时,Redis 监控中的 key 数量、占用内存下降:因为数据库已经存在较长时间,根据主动清理机制,其中应存在接近 25% 的已过期的 key。当我们遍历数据库中的所有 key 时,会触发被动清理机制,导致这些已过期的 key 被删除,从而使监控中的 key 数量和内存占用下降。当我们再次遍历该数据库时,key 数量和占用内存就不会下降了。

但是,当我们刚刚遍历完数据库后,Redis 中的 key 数量、占用内存逐渐上升恢复到之前的状态:因为在遍历完数据库后,该库中几乎不存在已过期的 key,此时主动清理机制几乎不可能超过 25%,每次只会有很少的已过期的 key 被清除。因此,随着新的 key 被逐渐写入,该数据库中的 key 数量和占用内存会逐渐上升,并恢复到存在接近 25% 的已过期的 key 的状态。

官方文档:https://redis.io/commands/expire/