Redis 内存管理与优化
内存是 Redis 最宝贵的资源。合理的内存管理能提升性能、避免 OOM、降低成本。本文将深入 Redis 内存管理机制,分享优化技巧。
一、内存架构
1.1 内存模型
Redis 内存布局:
┌─────────────────────────────────────┐
│ 数据区 │
│ - 键值对数据 │
│ - 过期时间 │
│ - 统计信息 │
├─────────────────────────────────────┤
│ 缓冲区 │
│ - 客户端输入缓冲 │
│ - 客户端输出缓冲 │
│ - 复制缓冲 │
├─────────────────────────────────────┤
│ 内存碎片 │
│ - 分配器开销 │
│ - 碎片空间 │
└─────────────────────────────────────┘
1.2 内存分配器
Redis 支持多种分配器:
- jemalloc(默认):碎片少,性能好
- libc:系统默认
- tcmalloc:Google 开发
查看分配器:
INFO memory | grep allocator
配置编译:
# 使用 jemalloc
make MALLOC=jemalloc
# 使用 libc
make MALLOC=libc
# 使用 tcmalloc
make MALLOC=tcmalloc
二、内存使用分析
2.1 内存指标
INFO memory
# 关键指标
used_memory: 104857600 # Redis 分配内存
used_memory_rss: 125829120 # 操作系统分配内存
used_memory_peak: 157286400 # 峰值内存
used_memory_lua: 1024 # Lua 引擎内存
maxmemory: 2147483648 # 最大内存限制
maxmemory_human: "2.00G"
mem_fragmentation_ratio: 1.20 # 碎片率
mem_allocator: jemalloc # 分配器
2.2 内存分析工具
# 内存统计
MEMORY STATS
# 输出示例
# 1) "peak.allocated"
# 2) (integer) 157286400
# 3) "total.allocated"
# 4) (integer) 104857600
# 5) "startup.allocated"
# 6) (integer) 1048576
# 7) "replication.backlog"
# 8) (integer) 1048576
# 9) "clients.slaves"
# 10) (integer) 0
# 11) "clients.normal"
# 12) (integer) 2097152
# 13) "aof.buffer"
# 14) (integer) 0
# 15) "lua.caches"
# 16) (integer) 0
# 17) "dbnum"
# 18) (integer) 1
# 19) "db.0"
# 20) (integer) 100000
# 21) "overhead.total"
# 22) (integer) 5242880
# 23) "keys.count"
# 24) (integer) 100000
# 25) "keys.bytes-per-key"
# 26) (integer) 1048
# 27) "dataset.bytes"
# 28) (integer) 99614720
# 29) "dataset.percentage"
# 30) "95.00%"
# 31) "peak.percentage"
# 32) "66.67%"
2.3 单 Key 内存分析
# 查看 Key 内存占用
MEMORY USAGE key
# 示例
MEMORY USAGE user:1001
# (integer) 1024 # 1KB
MEMORY USAGE large_list
# (integer) 10485760 # 10MB
2.4 大 Key 扫描
# 扫描大 Key
redis-cli --bigkeys
# 输出示例
# Scanning the entire keyspace...
#
# Biggest string found so far '"key1"' with 1048576 bytes
# Biggest list found so far '"key2"' with 10000 items
# Biggest set found so far '"key3"' with 5000 items
# Biggest hash found so far '"key4"' with 3000 items
# Biggest zset found so far '"key5"' with 2000 items
#
# Summary:
# Total keys: 100000
# Total memory: 100MB
# Biggest key: key1 (1MB)
三、内存淘汰策略
3.1 淘汰策略配置
# 配置淘汰策略
maxmemory-policy allkeys-lru
# 可选策略
# noeviction: 不淘汰(写操作返回错误)
# allkeys-lru: 淘汰最近最少使用的键
# volatile-lru: 淘汰最近最少使用的有过期时间的键
# allkeys-random: 随机淘汰
# volatile-random: 随机淘汰有过期时间的键
# volatile-ttl: 淘汰剩余时间最短的键
# allkeys-lfu: 淘汰最不常用的键(Redis 4.0+)
# volatile-lfu: 淘汰最不常用的有过期时间的键
3.2 LRU 算法
Redis LRU 实现:
- 近似 LRU(不是完整 LRU)
- 随机采样优化
- 采样数量可配置
配置:
maxmemory-samples 10 # 采样数量(越大越精确,越小越 CPU 友好)
LRU 演进:
Redis 2.8: 近似 LRU
Redis 3.0: 增加采样数量配置
Redis 4.0: 增加 LFU 策略
3.3 LFU 算法
# LFU 配置
maxmemory-policy allkeys-lfu
lfu-log-factor 10 # 对数因子
lfu-decay-time 1 # 衰减时间(分钟)
LFU vs LRU:
LRU: 基于时间(最近使用)
LFU: 基于频率(最常使用)
场景:
- 热点数据稳定 → LFU
- 热点数据变化快 → LRU
3.4 淘汰监控
# 查看淘汰统计
INFO stats
# 输出示例
# evicted_keys: 1000 # 被淘汰的 Key 数量
# keyspace_hits: 4500000 # 命中数
# keyspace_misses: 500000 # 未命中数
# 实时查看淘汰
MONITOR | grep -E "DEL|EXPIRE"
四、内存碎片
4.1 碎片率计算
碎片率 = used_memory_rss / used_memory
理想值:1.0-1.1
> 1.5: 碎片过多
< 1.0: 可能使用 swap
4.2 碎片产生原因
1. 频繁修改值的大小
2. 删除大量 Key
3. 内存分配器特性
4. 长期运行未重启
4.3 碎片整理
主动碎片整理:
activedefrag yes
active-defrag-ignore-bytes 100mb
active-defrag-threshold-lower 10
active-defrag-threshold-upper 100
active-defrag-cycle-min 1
active-defrag-cycle-max 25
active-defrag-max-scan-fields 1000
手动整理:
# 重启 Redis(最彻底)
redis-cli SHUTDOWN
redis-server /etc/redis/redis.conf
# 内存诊断
MEMORY DOCTOR
# 输出示例
# Samplers: 10
# * High memory fragmentation detected
# * Consider enabling active defragmentation
五、内存优化技巧
5.1 数据结构优化
# 1. 使用 Hash 替代 String
# ❌ 不推荐
SET user:1001 '{"id":1001,"name":"John","age":25}'
# ✅ 推荐
HSET user:1001 id 1001 name John age 25
# 节省 ~40% 内存
内存对比:
String (JSON): ~50 bytes + JSON 开销
Hash (ziplist): ~30 bytes
5.2 编码优化
# 查看编码
OBJECT ENCODING key
# 优化配置
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
5.3 缩短 Key 名
# ❌ 不推荐
SET user:1001:profile:name "John"
# ✅ 推荐
SET u:1001:p:n "John"
# 或使用 Hash
HSET u:1001:p n "John"
节省计算:
原始:user:1001:profile:name (22 字节)
优化:u:1001:p:n (9 字节)
节省:~60%
5.4 设置过期时间
# 临时数据设置过期
SET temp_key value EX 3600
# 批量设置过期
SCAN 0 MATCH temp:* COUNT 100
# 对每个 Key 设置 EXPIRE
5.5 避免大 Key
# 大 Key 危害:
# 1. 内存占用大
# 2. 操作慢
# 3. 网络传输慢
# 4. 删除阻塞
# 优化方案:
# 1. 拆分大 Key
# ❌ HSET large_hash field1 ... field10000
# ✅ 分片
HSET large_hash:0 field1 ... field1000
HSET large_hash:1 field1001 ... field2000
# 2. 使用合适的数据结构
# ❌ List 存储 10 万元素
# ✅ 分片或使用其他结构
5.6 定期清理
# 1. 清理过期数据
SCAN 0 MATCH temp:* COUNT 100
DEL temp:key
# 2. 清理无用数据
KEYS pattern:* # 开发环境
DEL pattern:*
# 3. 使用 UNLINK(异步删除)
UNLINK large_key # 非阻塞删除
六、监控与告警
6.1 监控指标
关键指标:
- used_memory / maxmemory: 内存使用率
- mem_fragmentation_ratio: 碎片率
- evicted_keys: 淘汰 Key 数
- expired_keys: 过期 Key 数
6.2 告警配置
# Prometheus 告警规则
- alert: RedisMemoryHigh
expr: redis_memory_used_bytes / redis_max_memory_bytes > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "Redis memory usage is high"
- alert: RedisFragmentationHigh
expr: redis_mem_fragmentation_ratio > 1.5
for: 10m
labels:
severity: warning
annotations:
summary: "Redis memory fragmentation is high"
- alert: RedisEvictionHigh
expr: increase(redis_evicted_keys_total[5m]) > 100
for: 5m
labels:
severity: warning
annotations:
summary: "Redis key eviction is high"
6.3 容量规划
容量规划步骤:
1. 统计当前内存使用
2. 预估增长趋势
3. 设置安全阈值(80%)
4. 提前扩容
示例:
当前:2GB,使用率 60%
月增长:10%
6 个月后:2GB * 1.6 = 3.2GB
建议配置:4GB
七、最佳实践
7.1 配置建议
# 内存配置
maxmemory 4gb
maxmemory-policy allkeys-lru
maxmemory-samples 10
# 碎片整理
activedefrag yes
active-defrag-threshold-lower 10
active-defrag-threshold-upper 100
# 数据结构优化
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
7.2 运维建议
日常运维:
1. 监控内存使用率
2. 定期扫描大 Key
3. 分析内存碎片
4. 清理过期数据
5. 容量规划
定期任务:
- 每周:大 Key 扫描
- 每月:内存分析
- 每季:容量评估
7.3 故障处理
# 内存 OOM
# 1. 临时增加内存
CONFIG SET maxmemory 8gb
# 2. 清理数据
MEMORY DOCTOR
UNLINK large_key
# 3. 重启(最后手段)
redis-cli SHUTDOWN
redis-server
总结
Redis 内存管理核心要点:
| 优化方向 | 关键配置 | 效果 |
|---|---|---|
| 淘汰策略 | maxmemory-policy | 避免 OOM |
| 碎片整理 | activedefrag | 减少浪费 |
| 数据结构 | Hash vs String | 节省 40% |
| Key 命名 | 缩短 Key 名 | 节省 60% |
| 过期清理 | EXPIRE | 自动释放 |
最佳实践:
- 合理设置 maxmemory
- 选择合适的淘汰策略
- 启用主动碎片整理
- 优化数据结构
- 定期扫描大 Key
- 监控内存指标
掌握内存管理,高效使用 Redis!