广告位联系
返回顶部

Redis过期键删除与内存淘汰策略

Mysql 来源:互联网 作者:佚名 发布时间:2026-10-14 22:05:58 人浏览
摘要

使用 Redis 做缓存时,我们通常不会希望一条缓存永久存在。 例如用户信息可以缓存 30 分钟: 1 2 SET user:1001 ... EXPIRE user:1001 1800 30 分钟以后,这个 Key 就过期了。 但这里会产生一个很有意思的

使用 Redis 做缓存时,我们通常不会希望一条缓存永久存在。

例如用户信息可以缓存 30 分钟:

1

2

SET user:1001 "..."

EXPIRE user:1001 1800

30 分钟以后,这个 Key 就过期了。

但这里会产生一个很有意思的问题:

时间到了以后,Redis 是怎么知道这个 Key 过期了,又是什么时候真正把它删除的?

另外还有一个经常和过期删除混在一起的问题:

如果 Key 都没过期,但是 Redis 内存已经满了,又应该删除谁?

这实际上是两套完全不同的机制:

1

2

3

4

5

6

7

8

Key 到期

   ↓

过期删除策略

 

 

内存不足

   ↓

内存淘汰策略

1. 什么是 TTL?

TTL 全称是 Time To Live,也就是 Key 还能存活多长时间。

例如:

1

2

SET token:1001 "abc"

EXPIRE token:1001 60

表示 token:1001 在 60 秒以后过期。

可以使用:

1

TTL token:1001

查看剩余时间。

如果返回 45,就表示还剩大约 45 秒。

TTL 还有两个比较特殊的返回值:-1 表示 Key 存在,但是没有设置过期时间;-2 表示 Key 已经不存在。

Redis 还支持直接在 SET 时设置 TTL:

1

SET token:1001 "abc" EX 60

也支持毫秒级的 PEXPIRE、PTTL。

2. Key 到期以后会立刻被删除吗?

不一定。

假设:

1

SET code:1001 "9527" EX 60

60 秒过去以后,从逻辑上 code:1001 已经过期,不应该再被用户读取。

但是这不意味着时间刚到第 60 秒的那一瞬间,Redis 一定立刻执行了一次删除操作。

如果 Redis 给每一个 Key 都设置一个定时器:

1

2

3

4

5

6

7

8

9

10

11

Key A 到时间

↓

触发定时器

↓

删除

 

Key B 到时间

↓

触发定时器

↓

删除

假设存在几百万个带 TTL 的 Key,就需要维护大量定时任务,成本很高。

因此 Redis 并没有采用“每一个 Key 一个定时器”的方式,而是把过期删除主要分成两种机制:

被动过期 + 主动过期。

我们平时也经常把被动过期称为 惰性删除。

3. 什么是惰性删除?

假设:

1

SET user:1001 "张三" EX 60

60 秒已经过去。

但是 Redis 可能暂时还没有真正把这个 Key 从内存结构中移除。

这时候客户端执行:

1

GET user:1001

Redis 发现 user:1001 已经过期,于是:

1

2

3

4

5

6

7

8

9

客户端访问 Key

      ↓

检查过期时间

      ↓

发现已经过期

      ↓

删除 Key

      ↓

返回不存在

这就是惰性删除。

可以简单理解:

平时先不管,等有人访问这个 Key 时,如果发现已经过期,再把它删除。

4. 只使用惰性删除行不行?

不行。

假设一个 Key:

1

SET temp:data "xxx" EX 60

60 秒以后过期。

但是从此以后再也没有任何客户端访问它。

那么如果只有惰性删除:

1

2

3

4

5

6

7

Key 已经过期

      ↓

没人访问

      ↓

永远不会触发检查

      ↓

一直占用内存

大量这样的 Key 积累以后,就会产生严重的内存浪费。

所以 Redis 还需要第二种机制:主动过期。

5. 什么是主动过期?

Redis 会周期性检查一部分设置了过期时间的 Key。

可以简单理解:

1

2

3

4

5

6

7

Redis 周期性运行过期检查

        ↓

从带 TTL 的 Key 中抽取一部分

        ↓

检查是否已经过期

        ↓

过期 → 删除

注意这里不是每隔一段时间扫描 Redis 中所有 Key。

如果 Redis 有一亿个 Key,每次都全量扫描,开销会非常大。

所以 Redis 采用的是:

抽样检查 + 根据过期情况继续检查。

这样可以在释放过期 Key 和避免 CPU 消耗过大之间取得平衡。

6. 为什么要惰性删除 + 主动过期一起使用?

因为两种策略刚好互相补充。

只有惰性删除的问题是:

没人访问的过期 Key 可能一直占内存。

如果主动过期每次都完整扫描所有 Key,又会导致 CPU 开销太大。

所以 Redis 采用:

1

2

3

4

5

6

7

8

9

惰性删除

↓

访问 Key 时顺手检查

 

        +

 

主动过期

↓

后台周期性抽样检查

既不用给每个 Key 创建单独定时器,也不需要不断全量扫描整个数据库。

7. 过期删除和内存淘汰是一回事吗?

不是。

这是这一篇最重要的区别。

假设 Redis 中有:

1

2

3

4

A:还有 10 分钟过期

B:还有 1 小时过期

C:永不过期

D:永不过期

现在 Redis 内存已经达到上限。

虽然这些 Key 一个都没有真正到期,Redis 仍然可能必须删除一部分数据,为新数据腾出空间。

这时候发生的不是 Expiration,而是 Eviction,也就是内存淘汰。

两者区别可以概括成:

机制 为什么删除 Key
过期删除 Key 的 TTL 到期
内存淘汰 Redis 达到 maxmemory,需要释放空间

所以一定要记住:

过期删除看时间,内存淘汰看内存压力。

8. 什么是 maxmemory?

Redis 可以通过 maxmemory 设置允许用于数据集的最大内存。

例如:

1

maxmemory 2gb

表示 Redis 使用的数据内存达到这个限制以后,需要根据配置决定接下来怎么办。

可以查看:

1

CONFIG GET maxmemory

也可以动态设置:

1

CONFIG SET maxmemory 2gb

当 Redis 达到 maxmemory 限制,需要继续写入新数据时,就会根据 maxmemory-policy 决定是否以及如何淘汰 Key。

9. 什么是 maxmemory-policy?

maxmemory-policy 决定:

内存满了以后,到底淘汰谁?

例如:

1

maxmemory-policy allkeys-lru

表示 Redis 达到内存限制以后,从所有 Key 中选择近似最久没有使用的数据进行淘汰。

常见策略主要围绕 LRU、LFU、Random、TTL 等方式展开。

10. 什么是 noeviction?

noeviction 的意思很直接:

谁也不淘汰。

当 Redis 达到 maxmemory 后,再执行需要增加数据的命令时,会返回错误。

可以理解:

1

2

3

4

5

Redis 内存达到上限

       ↓

不删除任何 Key

       ↓

新的写入无法继续增加数据

这种策略适合数据不能随便丢失的场景。

如果 Redis 本身就是纯缓存,则通常会考虑允许淘汰旧缓存。

11. 什么是 allkeys-lru?

allkeys-lru 可以拆开理解。

allkeys 表示所有 Key 都可能成为淘汰候选。

LRU 是 Least Recently Used,也就是最近最少使用。

例如:

1

2

3

4

A:刚刚访问过

B:1 秒前访问

C:10 分钟没访问

D:1 小时没访问

内存满时,Redis 会倾向于淘汰类似 D 这样的 Key。

所以 allkeys-lru 很适合经典缓存场景:

热点数据经常访问,冷数据逐渐被淘汰。

12. Redis 的 LRU 是严格 LRU 吗?

不是。

如果想实现完全精确的 LRU,就需要维护严格的访问顺序:

1

2

3

4

5

6

7

8

9

最近访问

   ↓

A

B

C

D

E

   ↓

最久没访问

每一次读写都可能需要维护这个结构,会增加额外的内存和 CPU 开销。

Redis 使用的是近似 LRU。

Redis 会对部分 Key 进行采样,再从候选 Key 中选择更符合淘汰条件的对象。

所以不能理解成:

Redis 每次一定能够找到整个数据库中绝对最久没有访问的那个 Key。

它追求的是:

用较低的开销获得足够接近 LRU 的效果。

13. 什么是 allkeys-lfu?

LFU 全称是 Least Frequently Used。

与 LRU 的区别在于:

LRU 关注最近多久没有访问。

LFU 关注访问频率高不高。

例如:

1

2

3

4

5

6

7

Key A:

刚刚访问过

但历史上一共只访问过 2 次

 

Key B:

稍早访问

但是一直被频繁访问

使用 LRU 时,A 看起来更加“新”。

使用 LFU 时,B 会被认为更加热门。

所以可以简单记:

LRU 看“最近什么时候用过”,LFU 看“经常不经常用”。

Redis 的 LFU 还会进行频率衰减,避免一个过去特别热门、现在已经无人访问的 Key 永远保持很高权重。

14. LRU 和 LFU 怎么选?

如果业务认为:

最近访问过的数据,更可能在接下来继续被访问。

可以考虑 LRU。

如果业务中的热点比较稳定,认为:

长期访问频率更能体现 Key 的价值。

可以考虑 LFU。

例如某条新闻上午突然爆火、下午已经无人访问,如果只记录历史访问总次数,它可能一直显得很热门。

所以 Redis 的 LFU 会随着时间对访问频率进行衰减。

15. 什么是 allkeys-random?

allkeys-random 就是从所有 Key 中随机淘汰。

例如:

1

2

3

4

5

A

B

C

D

E

Redis 可能随机选择 C 删除。

它不关心这个 Key 最近有没有访问,也不关心它的访问频率。

算法简单,但如果缓存存在明显冷热差异,通常没有 LRU/LFU 那么有针对性。

16. 什么是 volatile?

前面讲的是 allkeys-lru、allkeys-lfu、allkeys-random。

其中 allkeys 表示:

所有 Key 都可以成为候选。

而 volatile-* 只会从设置了 TTL 的 Key 中进行选择。

例如:

1

2

3

4

A:设置 TTL

B:设置 TTL

C:永久 Key

D:永久 Key

如果使用 volatile-lru,Redis 只会在 A 和 B 中按照 LRU 思路选择淘汰对象。

C 和 D 不参与。

所以可以直接记:

1

2

3

4

5

6

7

allkeys

↓

所有 Key 都参与

 

volatile

↓

只有设置 TTL 的 Key 参与

17. volatile-lru、volatile-lfu、volatile-random

这三个其实很好理解。

volatile-lru:只在带 TTL 的 Key 中淘汰最近最少使用的。

volatile-lfu:只在带 TTL 的 Key 中淘汰访问频率较低的。

volatile-random:只在带 TTL 的 Key 中随机淘汰。

所以真正需要理解的是:

前半部分决定候选范围,后半部分决定具体怎么选。

18. 什么是 volatile-ttl?

volatile-ttl 同样只考虑设置了过期时间的 Key。

但是它不看 LRU,也不看访问频率,而是优先选择剩余 TTL 更短的 Key。

例如:

1

2

3

Key A:还剩 10 秒

Key B:还剩 5 分钟

Key C:还剩 1 小时

A 本来就快过期,因此更适合作为优先淘汰对象。

19. Redis 到底有多少种淘汰策略?

很多老的 Redis 面试资料会总结成经典的 8 种:

1

2

3

4

5

6

7

8

noeviction

allkeys-lru

allkeys-lfu

allkeys-random

volatile-lru

volatile-lfu

volatile-random

volatile-ttl

但是这个说法和 Redis 版本有关。

较新的 Redis 又增加了 LRM(Least Recently Modified),例如 allkeys-lrm 和 volatile-lrm。

LRM 关注的是 Key 最近一次修改时间,而不是最近一次访问时间。

所以学习时不要把:

Redis 永远只有 8 种淘汰策略

当成绝对结论。

更重要的是理解这些策略的分类方式。

20. volatile 系列有一个坑

假设 Redis 使用:

1

maxmemory-policy volatile-lru

但是数据库中的所有 Key 都没有设置 TTL。

那么 volatile-lru 根本找不到任何候选对象。

最终效果会类似 noeviction:

Redis 无法通过淘汰 Key 继续释放空间,新的写入可能因为达到内存限制而失败。

所以使用 volatile-* 的前提是:

Redis 中确实存在足够的带 TTL Key。

21. 缓存场景应该怎么选?

如果 Redis 完全用于缓存,数据丢失后可以从数据库重新构建,那么通常可以考虑 allkeys-lru 或 allkeys-lfu。

如果 Redis 中同时存在:

1

2

3

可以淘汰的缓存

+

不能随便淘汰的数据

可以利用 TTL 与 volatile-* 进行一定程度的区分。

不过从架构设计上来说,缓存数据和重要数据如果淘汰要求完全不同,往往更适合拆分到不同 Redis 实例中。

22. TTL 到期和 LRU 淘汰有什么本质区别?

假设:

1

SET user:1 "A" EX 3600

这个 Key 还有 30 分钟才过期。

但是 Redis 内存满了,并且使用 allkeys-lru。

如果这个 Key 很久没有访问,那么:

即使 TTL 还有 30 分钟,它也可能提前被淘汰。

所以:

1

2

3

4

5

6

7

8

TTL

↓

最多允许存在多久

 

 

Eviction

↓

内存不够时可能提前淘汰

因此设置 1 小时 TTL,并不代表这个 Key 一定会存在整整 1 小时。

23. Key 过期了为什么内存没有马上下降?

Key 从逻辑上已经到期,不代表那一瞬间一定已经完成实际删除。

如果这个 Key 没有再次被访问,惰性删除就不会触发。

主动过期又采用抽样和渐进处理,并不是到某个时间点突然扫描所有 Key。

所以短时间内可能出现:

1

2

3

4

5

一些 Key 已经过期

       ↓

暂时还没有被清理

       ↓

仍占用一部分内存

随着主动过期继续执行,或者这些 Key 再次被访问,它们会逐渐被删除。

24. 为什么大量 Key 同时过期可能有问题?

假设业务一次写入 100 万个缓存,并且全部设置:

TTL = 30 分钟

那么 30 分钟以后,大量 Key 会在非常接近的时间同时过期。

Redis 自身需要短时间处理大量过期数据。

同时业务发现缓存不存在,又可能大量请求数据库。

这就可能进一步引发后面要学习的 缓存雪崩。

因此大量同类缓存通常不会把 TTL 设置得完全一样,而是加入一定随机值。

例如:

1

TTL = 1800 + random(0, 300)

把过期时间适当打散。

25. EXPIRE 和淘汰策略要怎么区分?

例如面试中可能问:

Redis 设置了 EXPIRE,过期以后是通过 LRU 删除吗?

不是。

EXPIRE 属于 TTL 过期机制,主要通过惰性删除和主动过期进行处理。

LRU、LFU 则属于内存淘汰机制,主要在 Redis 达到 maxmemory、需要释放内存时发挥作用。

所以:

1

2

3

4

5

6

7

8

EXPIRE

↓

时间到了

 

 

LRU / LFU

↓

内存不够了

两者触发条件完全不同。

26. 整个流程

假设:

1

SET user:1001 "..." EX 3600

这个 Key 之后可能经历:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

创建 Key

   ↓

设置 TTL = 1 小时

   ↓

正常使用

   │

   ├──────────────────┐

   ↓                  ↓

TTL 到期             内存先满

   ↓                  ↓

过期机制             maxmemory-policy

   ↓                  ↓

惰性删除             LRU / LFU / Random ...

+

主动过期

   ↓                  ↓

删除 Key             淘汰 Key

所以一个 Key 消失,并不一定意味着 TTL 到期。

也可能是:

Redis 因为内存压力提前把它淘汰了。

27. 最容易搞错的几个地方

第一,Key 过期并不代表到期瞬间一定完成删除,Redis 会通过惰性删除和主动过期配合清理。

第二,过期删除和内存淘汰不是一回事。前者由 TTL 触发,后者由 maxmemory 压力触发。

第三,设置了 TTL 的 Key 也可能在 TTL 到期以前就因为内存不足被淘汰。

第四,Redis 的 LRU 和 LFU 都是近似实现,不是为了追求绝对精确而维护高成本的全局结构。

第五,volatile-* 只会从带 TTL 的 Key 中选择;如果没有满足条件的 Key,就可能无法通过淘汰继续释放空间。

第六,“Redis 有 8 种淘汰策略”是经典版本中的常见总结,具体策略数量需要结合 Redis 版本来看。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。

您可能感兴趣的文章 :

原文链接 :
相关文章
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计