✅Redis 的过期策略是怎么样的?

✅Redis 的过期策略是怎么样的?

典型回答

Redis 通过设置过期时间来控制键值对的生命周期。过期时间可以通过EXPIRE、EXPIREAT、PERSIST等命令设置,也可以在插入数据时直接设置过期时间。

Redis 的过期策略采用的是定期删除和惰性删除相结合的方式。


惰性删除

当一个 key 过期时,不会立即从内存中删除,而是在访问这个 key 的时候才会触发删除操作。Redis会首先检查该键是否设置了过期时间,如果设置了,再检查它是否已经过期。

  • 如果已过期,Redis会立即删除这个键,并返回 nil 给客户端(就像这个键不存在一样)。
  • 如果未过期,则正常返回键的值。

惰性删除只有在键被访问时才会进行过期检查,不会消耗额外的CPU时间来扫描那些长期不被访问的键。

但是,如果一个键已经过期,但永远不再被访问,那么它将永远留在内存中,成为“垃圾数据”,无法被释放。这其实是一种内存泄漏。

定期删除

为了避免惰性删除存在的内存泄漏问题,Redis还提供了定期删除的功能。

Redis 默认每隔 100ms 就随机抽取一些设置了过期时间的 key,并检查其是否过期,如果过期才删除。也就是说每次执行时,并不是扫描所有设置了过期时间的键,而是从相应的过期字典中随机抽取一部分键进行检查。

定期删除是 Redis 的主动删除策略,它可以确保过期的 key 能够及时被删除,但是会占用 CPU 资源去扫描 key,可能会影响 Redis 的性能。

Redis默认同时开启定期删除和惰性删除两种过期策略。


内存释放?

需要注意的是,即使Redis进行了内存回收操作,也不能完全保证被删除的内存空间会立即被系统回收。


Redis并不是直接使用mallocfree来分配和释放每一小块内存,而是使用了自己包装的内存分配器,默认是jemalloc,也可以是libcptmalloc2

内存分配器为了追求效率,会有以下行为:

  1. 缓存与池化:当Redis释放(删除)一个键值对时,它只是把这部分内存归还给了它内部的内存分配器。分配器会保留这块内存,并将其放入一个“空闲内存池”中,目的是为了在未来Redis需要分配新的内存时,可以快速地从池中取出复用,而无需每次都向操作系统申请(系统调用是昂贵的)。
  2. 碎片化:频繁的分配和释放不同大小的内存块,会导致内存碎片。即使有足够的总空闲内存,也可能因为找不到一块连续的、足够大的内存来满足新的大对象分配请求,从而导致实质上的内存浪费。
  3. 归还OS的延迟与不彻底:内存分配器通常不会每次释放内存都立即munmap(解除内存映射)还给操作系统。它只会在满足特定条件时,比如空闲内存块非常大,或者总空闲内存超过某个阈值时,才会将部分内存归还给OS。这是一个权衡,目的是为了性能。

所以,你可能会看到 info memory 命令输出的 used_memory 下降了(因为Redis释放了内存给分配器),但操作系统监测到的 RSS 却下降得很慢甚至不下降。