shop.example.com 的购物车、session 和商品缓存还在一台 Redis 上膨胀。监控开始报警:used_memory 逼近上限。本篇只回答:触顶后谁有资格被删、副本为何常常「看起来没在淘汰」,以及为什么淘汰解决不了单机容量上限。落盘见 持久化,主从与自动切主见 复制 / Sentinel,装不下再水平切开见 Cluster 哈希槽。缓存穿透 / 击穿 / 雪崩与 Cache-Aside 不在本篇范围。
系列压力阶梯(本篇钉住「淘汰」这一格):
内存报警时,是 OOM 崩掉还是开始丢 key?
App 持续
SET / HSET,Redis 进程的已用内存往上爬。你配置了 maxmemory(服务端允许数据占用的内存上限)之后,触顶时会发生什么,并不取决于「机器还剩多少物理 RAM」,而取决于 maxmemory-policy(触顶后的内存淘汰策略)。两条分叉很清楚:
- 拒写:策略是
noeviction(或不淘汰候选集为空)时,新的写命令失败,已有 key 尽量留下。
- 删 key:策略允许淘汰时,Redis 按规则挑出受害者删掉,腾出空间后再接受写。
还有一个运维边界:复制积压、持久化等缓冲区占用不计入与
maxmemory 的比较。因此上限要留余量——把 maxmemory 顶满整机可用内存,仍可能被缓冲区推到真实 OOM。触顶不是「机器必崩」,而是「按你设的策略:拒写,或删谁」。
allkeys 和 volatile 差在「谁有资格被删」
先看主载体:八种常用策略,比散文更省事。
policy | 谁有资格被删 | 触顶行为 |
noeviction | 无人 | 拒写;不主动删 key |
allkeys-lru | 全部 key | 近似 LRU:优先删「最久没被访问」的 |
allkeys-lfu | 全部 key | LFU:优先删「访问最不频繁」的 |
allkeys-random | 全部 key | 在全部 key 里随机删 |
volatile-lru | 仅带 expire 的 key | 在带过期时间的集合里做近似 LRU |
volatile-lfu | 仅带 expire 的 key | 在带过期时间的集合里做 LFU |
volatile-random | 仅带 expire 的 key | 在带过期时间的集合里随机删 |
volatile-ttl | 仅带 expire 的 key | 优先删「更接近过期」的 |
本地定义两句就够用:
- allkeys 系策略:候选集是整库 key;购物车、无 TTL 的 session、商品缓存都可能被删。
- volatile 系策略:候选集只含设置了过期时间(expire)的 key。若库里没有任何带 expire 的 key,
volatile-*的行为会接近noeviction——你以为会淘汰,实际却在拒写。
近似 LRU(Least Recently Used,最近最少使用)不会扫全库,而是用采样(
maxmemory-samples)估「谁更冷」。LFU(Least Frequently Used,最不经常使用)从 Redis 4.0 起可用,可用 lfu-log-factor / lfu-decay-time 调「多热才算热」。官方把 allkeys-lru 当作多数「访问服从帕累托分布」缓存场景的合理默认:少数热 key 留下,长尾冷 key 先让路。Redis 8.6+ 的 LRM,以及怎么用命中率调策略?(非主结论)
从 Redis 8.6 起还有
allkeys-lrm / volatile-lrm(Least Recently Modified,按写侧时间戳偏向更久未改的 key)。版本不够就不要当通用建议。调策略时看 INFO 的 keyspace_hits / keyspace_misses 与 evicted_keys:命中率差、淘汰猛,说明候选集或算法与访问形态不匹配。也可用 redis-cli --hotkeys 抽样看热点(本身不是容量扩容方案)。副本为什么常常「看起来没在淘汰」?
你在 master 上看到
evicted_keys 在涨,副本上却「安静」——这往往不是监控坏了。默认情况下,副本会忽略自己的
maxmemory,不在本地独立跑淘汰。主节点按策略删 key 后,会把合成的 DEL 复制给副本,副本跟着删。若把 replica-ignore-maxmemory 设为 no,副本才会按自己的 maxmemory 参与淘汰——这通常只适合可写副本或你明确接受主从可能短暂不一致、且写入幂等的场景,改之前要搞清楚后果。即便配置了 maxmemory,副本仍可能用掉比该上限更多的内存——要盯副本真实占用,避免物理机 OOM,而不能只看「策略名」。因果链是:App 写 master → master 触顶并淘汰 → 复制流带上删除 → 副本数据集跟随。副本自己不「另起一套 LRU」,所以仪表盘上常常「主在淘汰、从在跟」。
淘汰解决不了单机容量上限
淘汰回答的是:这一台内存顶满时,拒写还是丢谁。它不把键空间摊到多机,也不提高「整库还能装多少不相关的热数据」。
shop.example.com 若工作集已经稳定大于单机可承载(扣掉缓冲区余量之后),换
allkeys-lru 只会更频繁地删仍可能被访问的 key,换来更高的后端穿透与抖动——工作集并没有变小。要水平切开键空间,走到 Cluster 的 16384 哈希槽,而不是继续在单进程里赌淘汰算法。淘汰是单机泄压阀;分片才是容量扩容。
清单:触顶前确认的配置
因果收束:App 写入撑大内存 → 碰到
maxmemory → maxmemory-policy 决定拒写或在「全库 / 仅带 TTL」候选集里删 key → 默认由 master 淘汰并复制 DEL,副本不独自淘汰且可能超过 maxmemory → 淘汰管不了「数据集已经装不进一台」——那是分片问题。上线前自检:
已设置
maxmemory,并为复制 / 持久化等缓冲区留出主机余量maxmemory-policy 与业务一致:缓存可丢用 allkeys;必须保住无 TTL 键时慎用 volatile 或改用拒写若选
volatile-*,确认关键键确实带 expire;否则行为接近 noeviction监控
used_memory、evicted_keys、keyspace_hits / keyspace_misses,以及副本真实内存,防物理 OOM默认副本忽略
maxmemory 已理解;仅在明确需要时改 replica-ignore-maxmemory工作集长期大于单机上限时,转向分片(见 Cluster 篇),而不是只换淘汰策略
Sources
- Evidence rechecked: 2026-08-15
- Key eviction —
maxmemory/maxmemory-policy;volatile-*在无 expire key 时接近noeviction;allkeys-lru与帕累托场景;缓冲区不计入 maxmemory 比较并需留 RAM;近似 LRU 与maxmemory-samples;LFU(≥4.0)与lfu-log-factor/lfu-decay-time;LRM(≥8.6,仅扩展阅读);INFO的 hits/misses /evicted_keys
- Replication — Maxmemory on replicas — 副本默认忽略
maxmemory;主淘汰并复制DEL;replica-ignore-maxmemory no;副本可能使用超过maxmemory的内存
- 系列相关:持久化 · 复制 / Sentinel · Cluster 哈希槽
- Author:Ximou Zhao
- URL:https://ximouzhao.com/article/redis-maxmemory-eviction
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts







