当前位置:首页>排行榜>Redis 淘汰策略怎么选:先分清它是缓存还是数据库

Redis 淘汰策略怎么选:先分清它是缓存还是数据库

  • 更新时间 2026-11-08 00:12:22
Redis 淘汰策略怎么选:先分清它是缓存还是数据库

线上 Redis 出问题,第一反应通常是查大 Key、查慢查询、查连接数。查了一圈都正常,最后发现是内存满了,而淘汰策略要么没配过,要么配了一个只在纸面上生效的。

更麻烦的是它不报错。写不进去的时候应用日志里可能什么都没有,读依然能读,监控上实例也还活着。

之前写 《Redis 大 Key 优化:205KB 的 String 该不该拆》 时提过内存这块,这篇单独把淘汰策略拎出来说。它不是一个参数问题。

淘汰策略不是参数,是给这个实例定性

大多数人把 maxmemory-policy 当成调优项,试几个值看哪个不报错。其实它回答的是一个业务问题:这个 Key 丢了,你能不能从别处再拼出来。

能拼出来,Redis 对你就是个代收点。包裹放三天没人取,店员清掉腾地方,反正寄件人手里还有底单,丢了再发一次就行。

拼不出来,Redis 对你就是个仓库。货架满了就锁门拒收,但里面一件都不会扔。

两种都合理,选了就得认。真正出事的是选完之后忘了自己选的是哪个——当代收点用,却往里存了没有底单的东西;当仓库用,货架满了还在怪系统不收货。

按"丢了能不能重建"分,八种策略只剩三类

官方列了八种策略(allkeys-lru、allkeys-lfu、allkeys-random、volatile-lru、volatile-lfu、volatile-random、volatile-ttl、noeviction)。按失效后果归一下,其实只有三类。

第一类,所有 Key 都丢得起。allkeys-lru 按最近最少使用淘汰,allkeys-lfu 按访问频率淘汰,allkeys-random 纯随机。这是纯缓存场景,丢了回源数据库重建。访问分布集中的用 lfu 比 lru 更贴合热点,但差别要到一定量级才看得出来。

第二类,只有一部分 Key 丢得起。 带 volatile- 前缀的几种,淘汰范围只限带过期时间的 Key,没设 TTL 的它一个都不碰。适用的是混合实例:一部分是缓存,一部分是队列、锁、会话这类不能动的。

第三类,一个都不能丢。noeviction,满了直接拒绝写入。

分类清楚了,选型就不需要背表:先看你这个实例里有没有不能重建的 Key,有就不能用 allkeys 系列;再看这些 Key 有没有过期时间,没有就别碰 volatile 系列。

配了 volatile-ttl 照样 OOM,因为候选是空的

这是最容易翻车的一处。

volatile-* 系列的淘汰候选,只从设置了过期时间的 Key 里挑。如果这个实例里所有 Key 都没设 TTL,内存满了之后它一个候选都找不到,行为直接退化成 noeviction——拒写、报 OOM。

回到代收点的比喻:店员只清理贴了取件期限的包裹。可货架上全是从没贴过期限的长期寄存件,摆满了之后他没有任何东西可以清,只能在门口挂一块"暂停收件"。

你配置里明明写着 volatile-ttl,看起来是配了自动清理的。实际跑起来和 noeviction 一模一样。

判断方法很直接,看两个数:

# 当前策略和内存占用,确认是否真的顶到了上限redis-cli INFO memory | grep -E "used_memory_human|maxmemory_human|maxmemory_policy"# 淘汰计数,长期为 0 说明淘汰压根没发生过redis-cli INFO stats | grep evicted_keys

evicted_keys 一直是 0、used_memory 贴着 maxmemory、同时业务在报写失败,基本就是这个组合。

不配 maxmemory 就等于没有上限。64 位下默认值就是 0,不限制。Redis 会一直吃,直到把整机内存吃光,然后被系统的 OOM Killer 杀掉。

这跟"淘汰策略选错"是两回事:前者是容器没有边界,后者是边界内怎么腾地方。

主从切换之后策略也会变脸。副本默认不执行 maxmemory,只是接收主节点同步过来的数据。一旦主挂了、副本被提升为新主,它就开始按自己配置文件里的 policy 执行淘汰。

主节点配 allkeys-lru、从节点配 noeviction,切换那一刻淘汰行为就换了一套,而这个过程没有任何报错。所以副本的 policy 要跟主节点对齐。它不是备份配置,它是下一任主节点的配置。

定策略前先回答这三个问题

不用急着改参数,先把这三问答完。

这个 Key 丢了,能从数据库重建吗? 能,就往 allkeys 系列走;不能,就留在 noeviction,等于承认它就是数据源。

有没有一类 Key 绝对不能动? 分布式锁、任务队列、会话,这三类被淘汰的后果不是"慢一点",是"两个 worker 同时进临界区"或者"任务凭空消失"。混在缓存实例里就是定时炸弹,条件允许就单独拆一个实例,关掉淘汰。

所有 Key 都有过期时间吗? 没有,volatile 系列就是纸面配置,回到上一节那个场景。

三问答完,配置本身没几行:

maxmemory 4gb                 # 按机器内存的 50%-70% 给,留出 fork 和缓冲区maxmemory-policy allkeys-lru  # 纯缓存场景;混合实例另算maxmemory-samples 10          # LRU 采样数,默认 5,调大更准但更耗 CPU

这里的 4gb 是示例值,不是推荐值。要按你的实例实际数据量和机器规格算,抄数字没有意义。

改策略可以不停机,CONFIG SET maxmemory-policy allkeys-lru 立刻生效,再改配置文件持久化。

但改之前一定要确认里面存的是什么。从 noeviction 切到 allkeys-lru,等于把"响亮的失败"换成了"安静的删除"。对一个纯缓存实例,这是修复;对一个混着队列的实例,这是把宕机换成了丢数据。

把缓存当数据库的代价

我整理过的一个案例里,一个 6 主 6 从的电商集群,凌晨主节点所在的物理机内存耗尽触发切换,切换后大约 200 个用户的购物车数据没了。

直接原因有三层:

  • • 切换前约 500ms 的写入没刷盘
  • • 客户端重试队列里的写请求在切换后打到了旧主
  • • cluster-require-full-coverage 关着,故障期间没受影响的槽位继续可写,跨槽位的购物车写入一半成功一半失败

看着像集群配置问题,根子还是同一个判断:把 Redis 当成了权威存储。 购物车这种数据一旦以 Redis 为准,任何一次切换、任何一次淘汰,丢的都是真数据。反过来,如果 MySQL 是权威、Redis 只做缓存层,上面那三层原因最多让页面慢几秒。

内存淘汰策略只是这个判断的一个出口。你在配 maxmemory-policy 那一刻做的选择,和你决定购物车存哪儿,是同一个问题的两种问法。

最后三句

淘汰策略没有最优值,只有和你给这个实例的定位对不对得上。判断标准就一条:这个 Key 丢了能不能重建。配之前先答完那三个问题,比试遍八种策略快得多。

你手上那个 Redis,是代收点还是仓库?如果混着用,现在是拿什么兜底的?评论区说说场景,我看看有没有更省事的拆法。

#Redis #内存淘汰策略 #缓存设计 #后端开发

随机文章