强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Redis 完全指南 / 12 - 内存管理

内存管理

12.1 内存使用分析

# 查看内存使用概况
redis-cli INFO memory

# 关键指标
redis-cli INFO memory | grep -E "used_memory_human|used_memory_peak_human|mem_fragmentation_ratio|mem_allocator"
# used_memory_human:2.50G
# used_memory_peak_human:3.20G
# mem_fragmentation_ratio:1.15
# mem_allocator:jemalloc-5.3.0

内存指标说明

指标 说明 正常范围
used_memory Redis 分配器分配的内存总量
used_memory_rss 操作系统看到的内存占用
mem_fragmentation_ratio 内存碎片率(rss / used) 1.0 - 1.5
used_memory_peak 历史内存使用峰值
mem_allocator 内存分配器 jemalloc

⚠️ 注意:碎片率 > 1.5 说明内存碎片严重;< 1.0 说明使用了 swap,性能会急剧下降。

单个 Key 内存分析

# 查看 Key 的内存占用
MEMORY USAGE user:1001
# (integer) 128

# 采样估算(大 Key 推荐)
MEMORY USAGE user:1001 SAMPLES 100

12.2 内存淘汰策略

当内存达到 maxmemory 限制时,Redis 根据配置的淘汰策略决定如何处理新写入。

八种淘汰策略

策略 范围 算法 说明
noeviction 不淘汰任何 Key,新写入返回错误
allkeys-lru 所有 Key LRU 淘汰最近最少使用的 Key
allkeys-lfu 所有 Key LFU 淘汰最不经常使用的 Key
allkeys-random 所有 Key 随机 随机淘汰 Key
volatile-lru 有 TTL 的 Key LRU 淘汰有 TTL 的最近最少使用 Key
volatile-lfu 有 TTL 的 Key LFU 淘汰有 TTL 的最不经常使用 Key
volatile-ttl 有 TTL 的 Key TTL 淘汰 TTL 最小的 Key
volatile-random 有 TTL 的 Key 随机 随机淘汰有 TTL 的 Key

策略选择决策树

所有 Key 都是缓存吗?
├── 是 → allkeys-lru / allkeys-lfu
│        │
│        ├── 热点数据明显 → allkeys-lfu
│        └── 访问均匀 → allkeys-lru
│
└── 否(混合缓存和持久数据)
    └── volatile-lru / volatile-lfu
         │
         ├── 缓存有 TTL → volatile-lru
         └── 需要更精确 → volatile-lfu
# 查看当前淘汰策略
redis-cli CONFIG GET maxmemory-policy

# 设置淘汰策略
redis-cli CONFIG SET maxmemory-policy allkeys-lru

# 设置最大内存
redis-cli CONFIG SET maxmemory 4gb

LRU vs LFU

特性 LRU LFU
全称 Least Recently Used Least Frequently Used
淘汰依据 最近最少访问 访问频率最低
计数器 访问时间戳 访问频率计数
适合场景 访问模式稳定 有明显热点数据
冷数据问题 不会(看时间) 不会(看频率)

LFU 算法使用对数计数器(logarithmic counter),并随时间衰减:

# 查看 Key 的 LFU 访问频率
OBJECT FREQ user:1001
# (integer) 15

# LFU 相关配置
redis-cli CONFIG SET lfu-log-factor 10      # 计数器增长速度
redis-cli CONFIG SET lfu-decay-time 1       # 衰减时间(分钟)

12.3 内存优化技巧

1. 使用 Hash 代替多个 String

# ❌ 低效:每个用户字段一个 Key
SET user:1001:name "张三"
SET user:1001:age "25"
SET user:1001:city "北京"

# ✅ 高效:使用 Hash 存储
HSET user:1001 name "张三" age "25" city "北京"

Hash 在元素较少时使用 listpack 编码,内存效率极高。

2. 使用整数集合(intset)

# Set 元素都是整数且数量少时,使用 intset 编码(极省内存)
SADD myset 1 2 3 4 5
OBJECT ENCODING myset   # "intset"

3. 控制 Key 长度

# ❌ 长 Key
SET user:profile:information:1001:basic "data"

# ✅ 短 Key
SET u:1001 "data"

# 使用 Hash Tag 缩短长度
SET {u:1001}:n "张三"

4. 合理设置过期时间

# 不需要永久保存的数据都设置 TTL
SET session:abc "data" EX 3600
SET cache:hot "data" EX 300

5. 压缩 Value

# 使用压缩算法(如 gzip、snappy、lz4)压缩 Value
# Python 示例
import gzip, json, redis

r = redis.Redis()

data = json.dumps({"large": "data..."})
compressed = gzip.compress(data.encode())
r.set("key", compressed, ex=3600)

# 读取时解压
raw = r.get("key")
original = gzip.decompress(raw).decode()
# DEL 大 Key 会阻塞
DEL big_key

# UNLINK 异步删除,不阻塞
UNLINK big_key

7. 整数共享池

Redis 内部维护了一个 0-9999 的整数对象池,使用这些整数时不会创建新对象:

# 这些整数共享同一个对象
SET counter 100
SET another 100
# 两个 Key 指向同一个 100 的 RedisObject

12.4 大 Key 检测

大 Key 是 Redis 性能杀手之一,可能导致慢查询、主从同步延迟、内存不均等问题。

大 Key 定义

类型 大 Key 标准
String Value > 10 KB
Hash/Set/ZSet 元素数 > 5000 或 Value > 10 MB
List 元素数 > 10000 或 Value > 10 MB

检测方法

方法一:redis-cli –bigkeys

# 扫描所有 Key,找出每种类型中最大的 Key
redis-cli --bigkeys

# 输出示例:
# [00.00%] Biggest string found so far 'key:123' with 10485760 bytes
# [25.00%] Biggest hash found so far 'user:all' with 50000 fields
# [50.00%] Biggest set found so far 'tags:all' with 100000 members
# [75.00%] Biggest zset found so far 'leaderboard' with 50000 members
# [100.00%] Biggest list found so far 'queue:tasks' with 100000 items

# 慢速扫描(降低对生产环境的影响)
redis-cli --bigkeys --i 0.1    # 每次 SCAN 后休眠 0.1 秒

方法二:MEMORY USAGE 采样

# 对特定 Key 检查内存
MEMORY USAGE big_key SAMPLES 100

方法三:SCAN + MEMORY USAGE 脚本

import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

BIG_KEY_THRESHOLD = 10240  # 10 KB

cursor = 0
big_keys = []

while True:
    cursor, keys = r.scan(cursor, count=100)
    for key in keys:
        try:
            mem = r.memory_usage(key, samples=100) or 0
            if mem > BIG_KEY_THRESHOLD:
                key_type = r.type(key)
                big_keys.append({
                    'key': key,
                    'type': key_type,
                    'memory': mem,
                })
                print(f"Big Key: {key} | Type: {key_type} | Memory: {mem} bytes")
        except Exception as e:
            print(f"Error checking {key}: {e}")
    
    if cursor == 0:
        break

print(f"\nFound {len(big_keys)} big keys")

方法四:RDB 文件离线分析

# 使用 rdb-tools 离线分析 RDB 文件
pip install rdbtools python-lzf

# 生成 CSV 报告
rdb --command memory /var/lib/redis/dump.rdb --bytes 10240 --keys 100 > memory_report.csv

# 按内存排序
sort -t',' -k4 -nr memory_report.csv | head -20

大 Key 治理

# 1. 删除大 Key(使用 UNLINK 异步删除)
UNLINK big_key

# 2. 拆分大 Key
# 大 Hash → 多个小 Hash
HSCAN big_hash 0 COUNT 100
# 将字段分散到多个 Hash

# 3. 压缩 Value
# 对大 Value 进行压缩存储

# 4. 设置过期时间
EXPIRE big_key 3600

12.5 内存碎片整理

Redis 4.0+ 支持在线内存碎片整理:

# 开启碎片整理
redis-cli CONFIG SET activedefrag yes

# 碎片整理配置
redis-cli CONFIG SET active-defrag-ignore-bytes 104857600   # 碎片达到 100MB 才开始整理
redis-cli CONFIG SET active-defrag-threshold-lower 10        # 碎片率达到 10% 开始整理
redis-cli CONFIG SET active-defrag-threshold-upper 100       # 碎片率达到 100% 全力整理
redis-cli CONFIG SET active-defrag-cycle-min 1               # 整理占用 CPU 最小比例
redis-cli CONFIG SET active-defrag-cycle-max 25              # 整理占用 CPU 最大比例

📌 业务场景

场景一:缓存内存告警

# 监控内存使用率
redis-cli INFO memory | grep used_memory_human
# 接近 maxmemory 时触发告警

场景二:热点数据识别

# 使用 LFU 策略,淘汰冷数据
redis-cli CONFIG SET maxmemory-policy allkeys-lfu

场景三:大 Key 导致延迟

# 定期扫描大 Key
redis-cli --bigkeys --i 0.1

# 发现大 Key 后拆分或异步删除

🔗 払展阅读