缓存超量怎么办——缓存容量规划与淘汰策略
问题背景
Redis 是一个基于内存的数据库,内存是有限且昂贵的资源。当缓存数据量超过可用内存时,如果不加控制,Redis 会因 OOM(Out of Memory)被内核杀掉,或者发生大量 Swap 导致性能崩溃。
核心问题:缓存不可能无限存储,超量时必须做出取舍。
缓存容量规划
第一步:估算缓存容量
// 每条缓存数据的容量估算
// Key: "user:1001" ≈ 12 bytes
// Value: JSON字符串 ≈ 500 bytes
// Redis Dict 额外开销 ≈ 50 bytes
// 每条 ≈ 562 bytes
// 100 万用户 ≈ 562 MB
// 1000 万用户 ≈ 5.5 GB
需要考虑的系统开销:
– Redis 自身进程内存(~1MB 基础,加上持久化缓冲)
– 主从同步缓冲区(repl_backlog)
– 客户端输出缓冲区
– AOF 重写期间的内存翻倍
第二步:设置 maxmemory
# redis.conf
maxmemory 4gb
maxmemory-policy allkeys-lru
永远不要在无限制的情况下运行 Redis。
Redis 内存淘汰策略(Eviction Policies)
当内存达到 maxmemory 上限时,Redis 根据配置的策略淘汰现有数据,为新的写入腾出空间。
八种淘汰策略
| 策略 | 全称 | 行为 | 适用场景 |
|---|---|---|---|
| noeviction | 不淘汰 | 写入返回 OOM 错误 | 保证数据不丢,但会写失败 |
| allkeys-lru | 所有 Key LRU | 淘汰最近最少使用的 key | 最通用,推荐 |
| allkeys-lfu | 所有 Key LFU | 淘汰使用频率最低的 key | 热点温度差异明显的场景 |
| volatile-lru | 带过期 LRU | 仅在设置了 TTL 的 key 中 LRU | 有 TTL 的做缓存,无 TTL 的做持久化 |
| volatile-lfu | 带过期 LFU | 仅在设置了 TTL 的 key 中 LFU | 同上,但基于频率 |
| allkeys-random | 所有 Key 随机 | 随机淘汰数据 | 冷热分布均匀的场景 |
| volatile-random | 带过期随机 | 随机淘汰 TTL 的 key | 不常用 |
| volatile-ttl | 带过期 TTL | 淘汰剩余 TTL 最短的 key | 希望快过期的先走 |
如何选择
80% 的场景推荐 allkeys-lru,因为:
– 自动淘汰最不常用的数据,适应大多数业务
– 不需要人工设置 TTL(当然建议设)
– 新写入的数据有机会保留,不会刚写入就被淘汰
特殊场景:
– 如果希望”永不过期”的数据不受影响 → 用 volatile-lru,永不过期的 key 不会被淘汰
– 如果希望热点数据更抗淘汰 → 用 LFU 系列,高频访问的 key 即使很少用也保留
淘汰触发的连锁反应
缓存穿透变多
淘汰意味着数据丢失,下次请求会穿透到数据库。如果淘汰量过大:
大量 key 被淘汰 → 大量缓存穿透 → 数据库压力飙升 → 接口响应变慢 → 更多请求堆积
应对:监控淘汰数量(evicted_keys),设置告警阈值。
热点数据被踢
LRU 是近似算法,在内存紧张时可能误淘汰即将被访问的数据:
# Redis 的近似 LRU
# 默认采样 5 个 key,淘汰其中最旧的
# 提高采样率可以更精确,但消耗更多 CPU
maxmemory-samples 10
调到 10 对大多数系统足够,调太高(20+)收益递减。
最佳实践
1. 预留缓冲空间
不要把 maxmemory 设到物理内存的上限。留出 20~30% 给系统、持久化、主从同步:
物理内存 8G → maxmemory 设 5~6G
物理内存 16G → maxmemory 设 10~12G
2. 设置合理的 TTL
即使有淘汰策略,设置 TTL 仍然是好习惯:
// 有 TTL 的数据会被优先考虑淘汰(volatile 类策略)
// 也能避免永远不用的数据占据内存
redis.opsForValue().set(key, value, 30, TimeUnit.MINUTES);
3. 监控淘汰情况
关键监控指标:
info stats → evicted_keys: 被淘汰的 key 数量
info memory → used_memory / maxmemory 使用比例
阈值建议:
– 淘汰量 0 / 秒:健康
– 淘汰量 < 100 / 秒:内存偏紧,需要扩
– 淘汰量持续上升:紧急扩容
4. 冷热数据分离
将冷数据(很少访问的历史数据)迁移到更大的持久化存储,腾出内存给热数据。
面试要点
- 默认
maxmemory-policy noeviction会导致写入失败,生成环境一定要修改 allkeys-lru是最通用的选择,适用于大部分场景- 不要只看淘汰策略,容量规划和监控同样重要
- maxmemory 不是越大越好,要预留系统开销和突发流量
- 能说出
evicted_keys是判断内存是否够用的核心指标


暂无评论内容