本文是大数据系列第 47 篇,深入讲解 Redis 如何通过过期键管理和内存淘汰策略控制内存使用,确保服务稳定运行。
Redis 性能基准
在理想条件下,Redis 的性能参考值:
- 读操作:约 110,000 次/秒
- 写操作:约 81,000 次/秒
实际性能受网络延迟、数据结构复杂度等因素影响。这一高性能背后,内存管理是保障系统稳定的关键。
maxmemory 配置
未设置 maxmemory(默认值 0)
Redis 不限制内存使用,当系统物理内存耗尽时:
- 默认策略为
noeviction,拒绝所有写操作并返回 OOM 错误 - 适用于 key 数量可预测、数据不可丢失的场景
设置 maxmemory
# redis.conf 中配置
maxmemory 1024mb
# 运行时查询
CONFIG GET maxmemory
内存达到上限后,Redis 会根据 maxmemory-policy 自动淘汰数据。
键过期机制
设置过期时间
EXPIRE key seconds # 设置秒级 TTL
PEXPIRE key milliseconds # 设置毫秒级 TTL
EXPIREAT key timestamp # 在指定 UNIX 时间戳过期
TTL key # 查询剩余秒数(-1 表示永不过期,-2 表示键不存在)
PTTL key # 查询剩余毫秒数
过期键的典型应用场景
| 场景 | 推荐 TTL |
|---|---|
| 验证码 | 5-10 分钟 |
| 登录 Session | 30 分钟 ~ 数小时 |
| 数据库查询缓存 | 根据业务波动设定 |
| 分布式锁 | 业务处理时间 + 缓冲 |
| API 限流计数器 | 时间窗口大小 |
三种删除策略
Redis 采用惰性删除 + 主动定期删除的组合策略,兼顾 CPU 和内存的使用效率。
1. 惰性删除(Lazy Deletion)
键过期后不立即删除,等下次访问时检查是否已过期,过期则删除并返回空值。
- 优点:节省 CPU,不做无效扫描
- 缺点:过期键可能长期占用内存(尤其是从不被访问的 key)
2. 主动定期删除(Active Expiry)
Redis 周期性随机抽查一批带有 TTL 的 key,删除其中已过期的条目。频率由 hz 参数控制(默认每秒 10 次)。
- 优点:能清理”僵尸键”,控制内存增长
- 缺点:无法保证所有过期键都被及时清理
3. 定时删除(Scheduled Deletion)
为每个 key 创建一个定时器,到期立即删除。因为要为每个 key 维护定时器,CPU 开销极大,Redis 实际未使用此策略。
内存淘汰策略
当内存达到 maxmemory 上限且无法继续申请内存时,Redis 按 maxmemory-policy 决定如何淘汰数据。
maxmemory-policy allkeys-lru
8 种策略对比
| 策略 | 淘汰范围 | 淘汰算法 | 说明 |
|---|---|---|---|
noeviction | — | — | 拒绝写操作(默认) |
allkeys-lru | 全部 key | LRU | 淘汰最久未使用的 key |
volatile-lru | 有过期时间的 key | LRU | 在设有 TTL 的 key 中 LRU |
allkeys-lfu | 全部 key | LFU | 淘汰访问频率最低的 key |
volatile-lfu | 有过期时间的 key | LFU | 在设有 TTL 的 key 中 LFU |
allkeys-random | 全部 key | 随机 | 随机淘汰任意 key |
volatile-random | 有过期时间的 key | 随机 | 随机淘汰带 TTL 的 key |
volatile-ttl | 有过期时间的 key | TTL | 优先淘汰剩余存活时间最短的 key |
LRU 实现原理
Redis 并非维护严格的 LRU 链表,而是为每个 key 记录最后访问时间戳(lru_clock,精度为秒)。淘汰时随机抽取若干个 key(maxmemory-samples 控制样本数),选出其中 LRU 值最大(最久未访问)的 key 删除。这是一种近似 LRU,在节省内存的同时效果接近精确 LRU。
LFU 实现原理
LFU(Least Frequently Used)淘汰访问频率最低的 key,比 LRU 更能抵抗偶发的批量访问对热点判断的干扰。Redis 6.0 引入 LFU,使用 Morris Counter 对访问频率进行概率计数,并加入时间衰减因子,避免历史热 key 永远不被淘汰。
策略选型建议
| 场景 | 推荐策略 |
|---|---|
| 不确定数据访问模式 | allkeys-lru(最通用) |
| 有明显冷热数据分区 | allkeys-lru 或 allkeys-lfu |
| 所有数据重要性均等 | allkeys-random |
| 业务已为 key 设置合理 TTL | volatile-ttl |
| 缓存数据不可丢失 | noeviction(需配合告警扩容) |
生产实践建议
- 生产环境必须设置
maxmemory,避免 Redis OOM 影响宿主机其他服务 - 开启内存淘汰前,先用
INFO memory监控used_memory和maxmemory_human - 结合业务特征为 key 设置合理的 TTL,减少淘汰算法的压力
- 使用
maxmemory-samples调大样本数(如 10)可提升 LRU 精度,但会增加 CPU 开销