Redis内存优化策略
约 1966 字大约 7 分钟
redismemory
2025-05-12
Redis 作为内存数据库,内存是最宝贵也最昂贵的资源。本文从内存分析、数据结构选择、过期策略到内存碎片管理,系统性地介绍 Redis 内存优化的方法。
内存分析工具
MEMORY USAGE
# 查看单个 key 的内存占用(字节)
MEMORY USAGE mykey
# 指定采样数(对于集合类型更精确)
MEMORY USAGE myhash SAMPLES 10
# 返回值包含:key 本身 + RedisObject + 底层数据结构 + 分配器开销MEMORY DOCTOR
# Redis 内存健康诊断
MEMORY DOCTOR
# 可能的输出:
# "Sam, I have no memory problems"
# "High allocator fragmentation..."
# "High allocator RSS overhead..."
# "High total RSS overhead..."INFO memory
INFO memory
# 关键指标
# used_memory: Redis 分配器已分配的内存总量
# used_memory_rss: 操作系统视角的实际内存占用
# used_memory_peak: 历史内存使用峰值
# mem_fragmentation_ratio: 内存碎片率 = used_memory_rss / used_memory
# mem_allocator: 内存分配器 (jemalloc/libc/tcmalloc)MEMORY STATS
MEMORY STATS
# 详细内存分布
# peak.allocated: 峰值分配
# total.allocated: 当前分配
# startup.allocated: 启动时分配
# replication.backlog: 复制积压缓冲
# clients.slaves: 从节点缓冲
# clients.normal: 客户端缓冲
# aof.buffer: AOF 缓冲
# dataset.bytes: 数据集大小
# dataset.percentage: 数据集占比数据结构选择优化
小数据量使用紧凑编码
# 用 Hash 替代多个 String(当字段数量 ≤ hash-max-listpack-entries)
# 不推荐:
SET user:1001:name "Alice"
SET user:1001:age "25"
SET user:1001:city "Shanghai"
# 3 个 key,每个都有 RedisObject 开销
# 推荐:
HSET user:1001 name "Alice" age "25" city "Shanghai"
# 1 个 key,listpack 编码非常紧凑控制编码阈值
# Hash
hash-max-listpack-entries 128 # 字段数 ≤ 128 用 listpack
hash-max-listpack-value 64 # 值长度 ≤ 64B 用 listpack
# ZSet
zset-max-listpack-entries 128
zset-max-listpack-value 64
# Set
set-max-intset-entries 512 # 全整数且 ≤ 512 用 intset
set-max-listpack-entries 128
# List
list-max-listpack-size -2 # 单节点最大 8KB
list-compress-depth 1 # 两端各保留 1 个不压缩整数优化
# Redis 预创建 0-9999 的共享整数对象
# 这些整数不会额外分配内存
# 对比
SET k1 100 # 使用共享对象,int 编码
SET k2 100000 # 超出范围,仍是 int 编码但不共享
SET k3 "hello" # embstr 编码,额外 SDS 分配Key 命名优化
# 不推荐:冗长的 key 名
SET application:user-service:user-profile:user-id:1001:session-data "..."
# 推荐:简短但可辨识
SET u:1001:s "..."
# 节省效果:key 名 50 字节 vs 8 字节
# 100 万个 key 节省 ≈ 40 MB| 策略 | 示例 | 说明 |
|---|---|---|
| 缩写前缀 | u: 代替 user: | 统一缩写表 |
| 避免冗余 | u:1001 不要 user:user_id:1001 | 去除不必要的层级 |
| 分隔符统一 | 用 : 分隔 | 保持一致性 |
过期策略与内存淘汰
过期删除机制
maxmemory 淘汰策略
# 最大内存限制
maxmemory 4gb
# 淘汰策略
maxmemory-policy allkeys-lru| 策略 | 淘汰范围 | 算法 | 适用场景 |
|---|---|---|---|
| noeviction | 不淘汰 | - | 写入报错 OOM |
| allkeys-lru | 所有 key | LRU | 通用缓存(推荐) |
| allkeys-lfu | 所有 key | LFU | 热点数据场景(推荐) |
| allkeys-random | 所有 key | 随机 | 无明显热点 |
| volatile-lru | 有过期时间的 key | LRU | 混合使用(部分持久) |
| volatile-lfu | 有过期时间的 key | LFU | 混合使用 |
| volatile-random | 有过期时间的 key | 随机 | 混合使用 |
| volatile-ttl | 有过期时间的 key | TTL 最小 | 优先淘汰快过期的 |
LRU vs LFU
# LFU 相关配置
lfu-log-factor 10 # 频率计数器增长因子(越大增长越慢)
lfu-decay-time 1 # 频率衰减时间(分钟)
# 查看 key 的 LFU 频率
OBJECT FREQ mykey内存碎片管理
碎片率分析
# 碎片率 = used_memory_rss / used_memory
# 理想值:1.0 ~ 1.5
# > 1.5:碎片较多,浪费内存
# < 1.0:使用了 Swap,性能严重下降!
INFO memory
# mem_fragmentation_ratio: 1.32碎片产生原因
| 原因 | 说明 |
|---|---|
| 频繁修改不同大小的值 | 旧空间被释放,新空间重新分配 |
| 大量删除 key | 释放的空间不连续 |
| 分配器特性 | jemalloc 按固定大小类分配 |
在线碎片整理 (Redis 4.0+)
# 启用在线碎片整理
CONFIG SET activedefrag yes
# 碎片率超过此值才开始整理
CONFIG SET active-defrag-threshold-lower 10 # 碎片超过 10% 开始
# 碎片率超过此值全力整理
CONFIG SET active-defrag-threshold-upper 100
# CPU 使用限制
CONFIG SET active-defrag-cycle-min 1 # 最少使用 1% CPU
CONFIG SET active-defrag-cycle-max 25 # 最多使用 25% CPU
# 或者手动触发(Redis 7.0+)
MEMORY PURGELazy Free (异步释放)
Redis 4.0 引入 Lazy Free,在后台线程异步释放大对象的内存,避免主线程阻塞。
# DEL 的异步版本
UNLINK mykey # 异步删除
# 过期 key 异步释放
lazyfree-lazy-expire yes
# 淘汰策略异步释放
lazyfree-lazy-eviction yes
# 隐式删除异步释放(如 RENAME 覆盖旧值)
lazyfree-lazy-server-del yes
# 从库加载 RDB 前清空数据时异步释放
replica-lazy-flush yes
# 用户 DEL 命令等同于 UNLINK
lazyfree-lazy-user-del yes
# FLUSH 命令异步执行
lazyfree-lazy-user-flush yes大 Key 检测与处理
# 扫描大 key(推荐使用 redis-cli --bigkeys)
redis-cli --bigkeys
# 使用 SCAN 遍历(不阻塞)
redis-cli --bigkeys --i 0.01 # 每次 SCAN 间隔 0.01 秒
# 大 key 的危害
# 1. 内存不均衡(Cluster 模式下热点节点)
# 2. 阻塞主线程(DEL/expire 大对象)
# 3. 网络带宽压力(传输大 value)
# 4. 序列化/反序列化耗时大 Key 处理方案
| 场景 | 方案 |
|---|---|
| 大 Hash (>10K fields) | 拆分为多个小 Hash (hash tag) |
| 大 List (>10K elements) | 拆分为多个 List,用索引关联 |
| 大 String (>1MB) | 压缩后存储,或存入对象存储 |
| 删除大 Key | 使用 UNLINK 异步删除 |
综合优化清单
总结
- 使用
MEMORY USAGE和INFO memory定位内存使用瓶颈 - 优先使用紧凑编码(Hash 替代多 String,控制编码阈值)
- 选择合适的淘汰策略,
allkeys-lfu适合大多数缓存场景 - 监控碎片率,必要时启用
activedefrag - 启用 lazy free 防止大对象删除导致主线程阻塞
- 定期扫描大 key,及时拆分或清理
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于