*Redis 内存优化指南
Redis 是内存数据库,内存优化至关重要。本文介绍如何优化 Redis 的内存使用。
*内存使用分析
*查看内存统计
redis-cli info memory
关键指标:
| 指标 | 说明 |
|---|---|
used_memory |
Redis 分配器分配的总字节数 |
used_memory_human |
人类可读的 used_memory |
used_memory_rss |
操作系统看到的 Redis 驻留集大小 |
used_memory_peak |
峰值内存使用 |
used_memory_lua |
Lua 引擎使用的内存 |
mem_fragmentation_ratio |
内存碎片率(usedmemoryrss / used_memory) |
mem_allocator |
使用的内存分配器 |
*内存碎片
碎片率 = used_memory_rss / used_memory
- 1.0-1.5:正常
- > 1.5:可能存在内存碎片
- < 1.0:可能使用了交换(swap)
减少碎片:
- 重启 Redis(RDB/AOF 持久化后重启可重建内存)
- 使用主动碎片整理(Redis 4.0+)
- 使用 jemalloc 分配器
*主动碎片整理(Active Defragmentation)
Redis 4.0+ 支持:
# 启用主动碎片整理
redis-cli config set activedefrag yes
# 配置触发阈值
redis-cli config set active-defrag-threshold-lower 10
redis-cli config set active-defrag-threshold-upper 100
# 配置 CPU 使用率
redis-cli config set active-defrag-cycle-min 5
redis-cli config set active-defrag-cycle-max 75
*数据结构优化
*1. 使用合适的数据类型
| 场景 | 推荐类型 | 替代方案 |
|---|---|---|
| 简单键值 | String | - |
| 对象属性 | Hash | 多个 String |
| 列表/队列 | List | - |
| 去重集合 | Set | - |
| 排序集合 | Sorted Set | - |
| 位图操作 | Bitmap | Set |
| 计数器 | String (INCR) | Hash |
| 地理位置 | Geo | Sorted Set |
| HyperLogLog | 基数统计 | Set |
*2. 使用 Hash 存储对象
将对象存储为 Hash 而非多个 String 键:
# 不推荐(多个 String)
SET user:1000:name "John"
SET user:1000:age 30
SET user:1000:city "NYC"
# 推荐(单个 Hash)
HSET user:1000 name "John" age 30 city "NYC"
Hash 使用 ziplist 编码时非常内存高效。
*3. 配置 ziplist 阈值
Redis 对小集合使用 ziplist 编码(内存高效):
# Hash 使用 ziplist 的最大条目数
hash-max-ziplist-entries 512
# Hash 使用 ziplist 的最大值大小(字节)
hash-max-ziplist-value 64
# List 使用 ziplist 的最大条目数
list-max-ziplist-entries 512
list-max-ziplist-value 64
# Set 使用 intset 的最大条目数
set-max-intset-entries 512
# Sorted Set 使用 ziplist 的最大条目数
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
调整这些值以平衡内存使用和 CPU 性能。
*4. 使用整数集合(Intset)
当 Set 只包含整数时,Redis 使用 intset 编码,非常内存高效。
*5. 使用 Bitmap
对于大量布尔值,Bitmap 比 Set 节省大量内存:
# 设置用户 1000 在线
SETBIT online 1000 1
# 检查用户 1000 是否在线
GETBIT online 1000
# 统计在线用户数
BITCOUNT online
*6. 使用 HyperLogLog
对于基数统计(唯一计数),HyperLogLog 使用固定 12KB 内存:
PFADD visitors user1 user2 user3
PFCOUNT visitors
*键优化
*1. 使用短键名
键名也占用内存。使用缩写:
# 不推荐
user:profile:1000
# 推荐
u:p:1000
*2. 使用 Hash 分片
对于大量小键,使用 Hash 分片减少键数量:
# 将 user:1000 到 user:9999 的数据分片到 100 个 Hash
HSET user:10 name "John" age 30 # user:10 包含 user:1000-user:1099
*3. 设置过期时间
为临时数据设置 TTL:
EXPIRE session:12345 3600
*4. 使用 LRU/LFU 逐出
当内存达到 maxmemory 时,使用逐出策略:
# 配置 maxmemory
maxmemory 2gb
# 配置逐出策略
maxmemory-policy allkeys-lru
逐出策略:
| 策略 | 说明 |
|---|---|
noeviction |
不逐出,写入时返回错误 |
allkeys-lru |
逐出最近最少使用的键 |
allkeys-lfu |
逐出最不经常使用的键 |
allkeys-random |
随机逐出键 |
volatile-lru |
逐出设置了 TTL 的最近最少使用键 |
volatile-lfu |
逐出设置了 TTL 的最不经常使用键 |
volatile-random |
随机逐出设置了 TTL 的键 |
volatile-ttl |
逐出 TTL 最短的键 |
*配置优化
*1. 使用 jemalloc
jemalloc 通常比 glibc malloc 有更好的内存分配特性:
# 编译时指定
make MALLOC=jemalloc
*2. 禁用不必要的功能
# 如果不需要,禁用 Lua 脚本
# 如果不需要,禁用 AOF
# 如果不需要,禁用 RDB
*3. 压缩
对于大值,在客户端压缩数据:
import zlib
# 压缩
compressed = zlib.compress(json.dumps(data).encode())
r.set('key', compressed)
# 解压
decompressed = zlib.decompress(r.get('key'))
*监控内存使用
*使用 MEMORY 命令
# 查看键的内存使用
redis-cli memory usage mykey
# 查看键的内存统计
redis-cli memory stats
# 查看键的编码方式
redis-cli debug object mykey
*使用 --bigkeys
redis-cli --bigkeys
这会扫描数据库并报告每种数据类型的最大键。
*使用 --memkeys
redis-cli --memkeys
报告内存使用最多的键。
*内存优化检查清单
- [ ] 使用
info memory检查碎片率 - [ ] 使用
--bigkeys识别大键 - [ ] 使用 Hash 替代多个 String 存储对象
- [ ] 调整 ziplist 阈值
- [ ] 使用 Bitmap/HyperLogLog 替代 Set 进行统计
- [ ] 设置合理的
maxmemory和逐出策略 - [ ] 为临时数据设置 TTL
- [ ] 使用短键名
- [ ] 考虑启用主动碎片整理
- [ ] 在客户端压缩大值
- [ ] 使用 jemalloc 分配器
- [ ] 监控
used_memory_peak了解峰值使用