redis 进阶 - 内存优化
Redis内存优化可以从多个角度进行,包括:
- 数据结构优化
- 内存配置优化
- 数据过期策略
- 编码化
- 内用外部存储
内存结构分析
内存组成
# 查看内存详情
redis-cli> INFO memory
# 关键指标:
# used_memory: 11845632 # Redis分配器分配的内存
# used_memory_human: 11.30M
# used_memory_rss: 12419072 # 操作系统角度看的内存
# used_memory_peak: 15832432 # 内存使用峰值
# mem_fragmentation_ratio: 1.05 # 内存碎片率
# used_memory_overhead: 1021120 # Redis内部管理开销
# used_memory_dataset: 9832448 # 实际数据占用的内存
# 内存开销结构
- Redis进程本身:约1-3MB
- 内部数据结构开销:每个键值对约96字节
- 数据实际存储:取决于数据类型和编码
- 复制缓冲区、AOF缓冲区等
内存分配器
Redis 使用 Jemalloc 作为默认内存分配器:
# 查看分配器信息
redis-cli> INFO memory
# 相关参数:
# mem_allocator: jemalloc-5.1.0
# active_defrag_running: 0
# lazyfree_pending_objects: 0
# Jemalloc优点:
# 1. 减少内存碎片
# 2. 多线程性能好
# 3. 提供详细的内存统计
数据结构优化
键名优化
# 不好的做法:键名太长
"user:session:1234567890:profile:settings:theme"
# 内存占用:约60字节
# 优化做法:使用缩写或编码
"u:s:1234567890:p:st:th"
# 内存占用:约25字节
# 极致优化:使用数字ID
"u:1234567890"
# 内存占用:约15字节
# 注意:键名共享机制(Redis 7.0+)
# 相同的键名前缀会共享内存
字符串优化
# 场景:存储数字
# 不好的做法:存储为字符串
SET counter "1000000" # 占用7字节 + Redis开销
# 优化做法:存储为整数
SET counter 1000000 # 占用4字节 + Redis开销
# 检查编码方式
redis-cli> OBJECT ENCODING counter
# "int" - 更节省内存
# 场景:存储短字符串
# Redis内部使用SDS(Simple Dynamic String)
# SDS结构:
# struct sdshdr {
# int len; // 4字节
# int free; // 4字节
# char buf[]; // 实际数据
# }
# 总开销:数据长度 + 8字节 + 1字节(结束符)
Hash优化
# Hash的两种编码方式:
# 1. ziplist(压缩列表):元素少且值小
# 2. hashtable(哈希表):元素多或值大
# 配置参数(redis.conf):
hash-max-ziplist-entries 512 # 元素数量阈值
hash-max-ziplist-value 64 # 单个元素值大小阈值
# 优化示例:
# 存储用户信息
HMSET user:1000 name "John" age 30 city "NY" job "Engineer"
# 检查编码
redis-cli> OBJECT ENCODING user:1000
# "ziplist" - 如果满足条件
# 优化建议:
# 1. 字段名尽量短
# 2. 数值存储为整数
# 3. 控制字段数量
# 4. 大Hash拆分成小Hash
List优化
# List的三种编码:
# 1. ziplist:元素少且小
# 2. linkedlist:双向链表
# 3. quicklist(Redis 3.2+):ziplist组成的链表
# 配置参数:
list-max-ziplist-size -2 # 每个quicklist节点大小限制
# -2: 8KB, -1: 4KB, 正数: 元素个数
list-compress-depth 0 # 压缩深度
# 0: 不压缩, 1: 首尾各1个节点不压缩, ...
# 优化建议:
# 1. 列表元素较多时,使用quicklist
# 2. 大列表考虑分片
# 3. 使用LPUSH/RPOP代替LRANGE获取所有元素
集合 Set优化
# Set的两种编码:
# 1. intset:元素都是整数且数量少
# 2. hashtable:其他情况
# 配置参数:
set-max-intset-entries 512
# 优化示例:
# 存储用户标签
SADD user:1000:tags 1 2 3 4 5 # 使用数字ID代替字符串
# 检查编码
redis-cli> OBJECT ENCODING user:1000:tags
# "intset" - 更节省内存
有序集合 Sorted Set优化
# ZSet的两种编码:
# 1. ziplist:元素少且小
# 2. skiplist+dict:跳表+字典
# 配置参数:
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
# 优化建议:
# 1. 成员名尽量短
# 2. 分数使用整数
# 3. 大ZSet考虑分片
内存优化配置
内存淘汰策略
# redis.conf配置
maxmemory 4gb # 设置最大内存
maxmemory-policy volatile-lru # 淘汰策略
# 淘汰策略选项:
# volatile-lru: 从设置了过期时间的键中淘汰最近最少使用的
# allkeys-lru: 从所有键中淘汰最近最少使用的
# volatile-lfu: 从设置了过期时间的键中淘汰最不经常使用的
# allkeys-lfu: 从所有键中淘汰最不经常使用的
# volatile-random: 从设置了过期时间的键中随机淘汰
# allkeys-random: 从所有键中随机淘汰
# volatile-ttl: 淘汰剩余生存时间最短的
# noeviction: 不淘汰,返回错误(默认)
# 根据场景选择:
# 缓存场景:allkeys-lru 或 volatile-lru
# 持久化数据:noeviction + 监控告警
# 混合场景:volatile-lru + 关键数据不设置过期
内存压缩
# 字符串压缩(Redis 7.0+)
# list、hash、zset的quicklist节点可以压缩
list-compress-depth 1
# 0: 不压缩
# 1: 首尾各1个节点不压缩,其他压缩
# 2: 首尾各2个节点不压缩,其他压缩
# 以此类推
# 内存碎片整理
activedefrag yes
active-defrag-ignore-bytes 100mb # 内存碎片达到100MB时开始整理
active-defrag-threshold-lower 10 # 碎片率下限10%
active-defrag-threshold-upper 100 # 碎片率上限100%
active-defrag-cycle-min 5 # 最小CPU使用百分比
active-defrag-cycle-max 75 # 最大CPU使用百分比
过期键优化
# 过期键删除策略:
# 1. 定时删除:主动检查(默认每秒10次)
# 2. 惰性删除:访问时检查
# 配置优化:
hz 10 # 提高检查频率(默认10,范围1-500)
# 但会增加CPU使用
# 建议:
# 1. 避免同一时间大量键过期(设置随机过期时间)
# 2. 监控过期键数量
redis-cli> INFO stats
# expired_keys: 已过期的键总数
# expired_stale_perc: 过期比率
高级优化技巧
使用Bitmaps优化布尔型数据
# 场景:用户在线状态、打卡记录等
# 传统方式:为每个用户存储一个键
SET user:1000:online 1 # 约100字节
# Bitmaps方式:
SETBIT online:2023-10-01 1000 1 # 约1位 + 开销
# 1亿用户每天只需约12MB
# 操作示例:
# 设置用户1000在线
SETBIT online:2023-10-01 1000 1
# 检查是否在线
GETBIT online:2023-10-01 1000
# 统计在线人数
BITCOUNT online:2023-10-01
使用HyperLogLog优化基数统计
# 场景:统计UV(独立访客)
# 传统方式:使用Set存储用户ID
SADD uv:2023-10-01 "user1" "user2" "user3"
# 1亿用户需要约3.2GB
# HyperLogLog方式:
PFADD uv:2023-10-01 "user1" "user2" "user3"
# 1亿用户只需约12KB,误差约0.81%
# 获取统计结果
PFCOUNT uv:2023-10-01
# 合并多日数据
PFMERGE uv:week uv:2023-10-01 uv:2023-10-02
使用GEO优化地理位置
# GEO底层使用ZSet存储
# 配置优化:使用ziplist编码
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
# 示例:
GEOADD cities 116.405285 39.904989 "北京"
# 内存占用:约50字节/点
# 优化建议:
# 1. 精度控制:根据业务需求选择精度
# 2. 数据分片:按区域分片存储
内存分析
# 1. 查看整体内存
redis-cli> INFO memory
# 2. 查看键内存
redis-cli> MEMORY USAGE key
redis-cli> MEMORY STATS
# 3. 查看大键
redis-cli> --bigkeys
# 或使用scan方式避免阻塞
redis-cli> redis-cli --bigkeys -i 0.1 # 每100ms休息一次
# 4. 内存医生
redis-cli> MEMORY DOCTOR
# 5. 内存分析报告
redis-cli> MEMORY MALLOC-STATS

浙公网安备 33010602011771号