高级篇 10. Redis 最佳实践 - 内存安全配置与淘汰策略

1 阅读5分钟

📚 高级篇 10. Redis 最佳实践 - 内存安全配置与淘汰策略 (大结局)

一、 核心底线:设置内存警戒线 (maxmemory)

无论你的物理机内存有多大,绝对不能让 Redis 无限制地使用内存!

如果 Redis 吃光了机器的所有物理内存,Linux 操作系统的 OOM Killer 机制会毫不留情地把 Redis 进程直接杀掉。

✅ 大厂安全规范:

打开 redis.conf,必须显式配置最大内存上限。通常建议设置为物理内存的 50% ~ 75% (必须留出足够的内存给系统本身,以及应对上一节提到的 bgsave 写时复制的内存翻倍风险)。

Properties

# 限制 Redis 最多只能使用 10GB 内存
maxmemory 10gb

二、 生死抉择:当 10GB 真的存满时,该怎么办?

当 Redis 的内存使用量达到了 maxmemory 的设定值,此时如果 Java 客户端又发来了一个 set new_key new_value 的写入请求,Redis 必须做出决定。

Redis 提供了 8 种内存淘汰策略(由 maxmemory-policy 参数控制),它们主要分为三大流派:

1. 摆烂流:noeviction (默认策略)

  • 行为: 不淘汰任何数据。如果内存满了,对于所有申请更多内存的命令(如 set, hset),直接向客户端报错 (error) OOM command not allowed。读操作 (get) 还可以正常执行。
  • 适用场景: 如果你把 Redis 当作绝对不能丢数据的核心数据库来用,只能选这个。但这在企业级缓存架构中几乎是不可能的。

2. 局部清洗流:volatile-* (只针对设置了过期时间的 Key)

这套策略认为,那些没有设置过期时间(TTL)的 Key 是系统极其重要的长效数据(比如全局配置),绝对不能动。只能从设置了过期时间的 Key 里面挑人淘汰。

  • volatile-lru:挑出最近最少使用的淘汰。
  • volatile-lfu:挑出最不经常使用的淘汰。
  • volatile-ttl:谁的剩余存活时间 (TTL) 最短,就先淘汰谁(快死的人先送走)。
  • volatile-random:在设置了 TTL 的 Key 里随机抽签淘汰。

3. 无差别清洗流:allkeys-* (面向所有 Key 动刀) ⚡企业最常用

这套策略认为,既然是缓存,那所有的数据都是可以丢的。内存储满时,在全库所有的 Key 中进行选拔淘汰。

  • allkeys-lru:全库选出最近最少使用的淘汰。
  • allkeys-lfu:全库选出最不经常使用的淘汰。
  • allkeys-random:全库随机抽签淘汰(除非你真的完全不在乎命中率,否则别用)。

三、 面试终极拷问:LRU 与 LFU 的巅峰对决

在上述策略中,最核心的算法就是 LRU 和 LFU。只要是后端研发面试,这两个概念必考!

1. LRU (Least Recently Used) - 最近最少使用

  • 核心思想:时间维度淘汰。如果一个数据很久没被访问过了,那么它将来被访问的概率也很低,踢掉它!
  • 痛点 (偶发性污染): 假设有一个“历史文章列表”平时根本没人看。某天夜里爬虫疯狂扫描了这个列表,导致这些冷门数据的“最近访问时间”被刷新。LRU 就会误以为它们是热点数据,反而把那些真正的、只是刚好这几分钟没被点击的热门商品给踢出了内存!

2. LFU (Least Frequently Used) - 最不经常使用 (Redis 4.0 引入的杀器)

  • 核心思想:访问频率维度淘汰。它记录了每个 Key 被访问的次数。谁被访问的次数最少,谁就被踢掉。
  • 破局优势: 完美防御了上述爬虫带来的“缓存污染”问题。即使冷门数据被爬虫扫了一次,它的访问频率依然极低,在内存满时依然会被优先踢掉,从而死死保住了真正的热点商品数据。

(💡 关联思考:还记得我们在多级缓存篇章学过的 Caffeine 本地缓存吗?它引以为傲的 W-TinyLFU 算法,也是基于这种频率思想进化而来的!)


四、 企业级架构选型指南 (直接抄作业)

在真实的业务开发和系统搭建中,你应该怎么配?

  1. 强推荐策略:allkeys-lfu

    • 理由: 作为纯粹的分布式缓存池,使用 allkeys-lfu 能够达到最高效的缓存命中率,把最核心的热点数据死死留在内存中。

    Properties

    maxmemory-policy allkeys-lfu
    
  2. 妥协策略:volatile-lruvolatile-lfu

    • 理由: 只有当你的 Redis 里同时混杂着“绝对不能丢的业务状态数据(不设 TTL)”和“普通的缓存数据(设了 TTL)”时,为了保护状态数据,你才退而求其次选择 volatile 前缀的策略。但规范的架构应该将这两种数据拆分到不同的 Redis 实例中。

最佳实践篇 - 终极通关总结

从这几个章节中,你建立了一套极其坚实的工程防御体系:

  1. 防止单线程卡死: 拒绝 BigKey,用 UNLINK 异步删除,掌握五大数据结构的精准选型。
  2. 防止网络瘫痪: 使用 MSETPipeline 压缩网络往返耗时,掌握集群模式下的并行分组路由技术。
  3. 防止服务器崩溃: 关停危险命令,控制物理内存上限与持久化 fork 阻塞,利用 Slow Log 揪出性能内鬼,并用 LFU 算法兜底内存打满的极端情况。