一、概述
1.1 背景介绍
Redis 在生产环境中承担着缓存、会话存储、消息队列、分布式锁等多种角色。随着数据量增长和并发压力上升,内存碎片、持久化 I/O 抖动、慢查询堆积这三类问题会逐渐显现,直接影响服务延迟和稳定性。Redis 8.x 在内存管理和持久化机制上做了若干改进,但核心调优思路与 7.x 一脉相承。
1.2 技术特点
单线程命令处理:网络 I/O 多线程化(Redis 6.0+),命令执行仍为单线程,避免锁竞争但对大 Key 操作敏感
jemalloc 内存分配:默认使用 jemalloc 5.x,相比 libc malloc 碎片率更低,但长期运行仍需关注 mem_fragmentation_ratio
多种持久化模式:RDB 快照、AOF 追加日志、混合持久化三种模式各有适用场景,选错会导致性能或数据安全问题
1.3 适用场景
场景一:高并发缓存服务,QPS 10万+,需要精细控制内存淘汰策略
场景二:金融/支付类业务,对数据持久化有强要求,需要 AOF everysec 或混合持久化
场景三:大规模 Key 存储,存在 BigKey 风险,需要定期扫描和治理
1.4 环境要求
| 组件 | 版本要求 | 说明 |
|---|---|---|
| Redis | 8.0+ | 本文配置基于 Redis 8.x |
| 操作系统 | Linux Kernel 4.18+ | 需要支持 THP 控制、NUMA 绑定 |
| 内存 | 建议 16GB+ | maxmemory 设置为物理内存的 60-70% |
| 存储 | SSD(AOF 场景) | AOF rewrite 对磁盘 I/O 压力较大 |
二、详细步骤
2.1 内存模型与碎片率分析
2.1.1 理解 jemalloc 与内存碎片
Redis 使用 jemalloc 作为默认内存分配器。jemalloc 将内存按 size class 分配,小对象(<= 14KB)走 small bin,大对象走 large bin。这种设计在高频分配/释放场景下碎片率远低于 ptmalloc,但在 Key 大小分布极不均匀时仍会产生碎片。
通过 INFO memory 查看关键指标:
# 查看内存使用详情 redis-cli -h 127.0.0.1 -p 6379 INFO memory | grep -E "used_memory|mem_fragmentation|allocator" # 关键输出字段说明: # used_memory: Redis 实际使用的内存(字节) # used_memory_rss: 操作系统分配给 Redis 的内存(RSS) # mem_fragmentation_ratio: RSS / used_memory,正常范围 1.0~1.5 # allocator_frag_ratio: jemalloc 内部碎片率
碎片率判断标准:
mem_fragmentation_ratio < 1.0:内存被 swap,性能严重下降,需立即处理
1.0 ~ 1.5:正常范围
> 1.5:碎片率偏高,考虑开启主动碎片整理
2.1.2 主动碎片整理配置
Redis 4.0+ 引入 activedefrag,8.x 默认关闭,需根据实际情况开启:
# redis.conf 配置 activedefrag yes # 开启主动碎片整理 active-defrag-ignore-bytes 100mb # 碎片超过 100MB 才触发 active-defrag-threshold-lower 10 # 碎片率超过 10% 开始整理 active-defrag-threshold-upper 100 # 碎片率超过 100% 全力整理 active-defrag-cycle-min 1 # 最少占用 CPU 1% active-defrag-cycle-max 25 # 最多占用 CPU 25%
主动碎片整理会占用 CPU,在 CPU 敏感的场景下需要控制 active-defrag-cycle-max,避免影响命令处理延迟。
2.2 maxmemory 策略选择
2.2.1 各策略适用场景
# redis.conf maxmemory 8gb # 设置最大内存,建议为物理内存的 60-70% maxmemory-policy allkeys-lru # 淘汰策略
| 策略 | 淘汰范围 | 适用场景 |
|---|---|---|
| noeviction | 不淘汰,写入报错 | 数据不可丢失的持久化存储 |
| allkeys-lru | 所有 Key,LRU | 通用缓存,Key 无过期时间 |
| volatile-lru | 有 TTL 的 Key,LRU | 混合存储(部分持久 + 部分缓存) |
| allkeys-lfu | 所有 Key,LFU | 热点数据明显,访问频率差异大 |
| volatile-lfu | 有 TTL 的 Key,LFU | 同上,但只淘汰有过期时间的 Key |
| allkeys-random | 随机淘汰 | 访问模式均匀,无热点 |
| volatile-ttl | TTL 最短的 Key | 希望优先淘汰即将过期的数据 |
决策原则:纯缓存场景用 allkeys-lru;有热点数据且访问频率差异明显时用 allkeys-lfu;混合存储(既有缓存又有持久数据)用 volatile-lru,但要确保持久数据不设 TTL。
2.3 持久化策略选择
2.3.1 RDB vs AOF vs 混合持久化
RDB(快照):
# redis.conf save 900 1 # 900秒内有1个Key变更则触发 save 300 10 # 300秒内有10个Key变更则触发 save 60 10000 # 60秒内有10000个Key变更则触发 rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir /var/lib/redis
RDB 通过 fork() 子进程生成快照,对主进程影响取决于内存大小和 COW(Copy-On-Write)开销。内存 8GB 的实例,fork 耗时通常在 50~200ms,期间主进程会短暂阻塞。
AOF(追加日志):
# redis.conf appendonly yes appendfilename "appendonly.aof" appendfsync everysec # 每秒 fsync,丢失最多 1 秒数据 # appendfsync always # 每次写入 fsync,最安全但性能最差 # appendfsync no # 由 OS 决定,性能最好但可能丢失较多数据 no-appendfsync-on-rewrite yes # AOF rewrite 期间不 fsync,避免 I/O 竞争 auto-aof-rewrite-percentage 100 # AOF 文件增长 100% 时触发 rewrite auto-aof-rewrite-min-size 64mb # AOF 文件至少 64MB 才触发 rewrite
混合持久化(推荐生产使用):
# redis.conf(Redis 4.0+) aof-use-rdb-preamble yes # 开启混合持久化 appendonly yes appendfsync everysec
混合持久化在 AOF rewrite 时将当前数据以 RDB 格式写入 AOF 文件头部,后续增量以 AOF 格式追加。恢复速度接近 RDB,数据安全性接近 AOF everysec,是生产环境的最优选择。
2.3.2 持久化性能影响对比
| 模式 | 恢复速度 | 数据安全 | 写入性能影响 | 磁盘占用 |
|---|---|---|---|---|
| RDB only | 快 | 低(分钟级丢失) | 低(fork 有抖动) | 小 |
| AOF everysec | 慢 | 高(最多1秒) | 中 | 大 |
| AOF always | 慢 | 最高(无丢失) | 高(每次 fsync) | 大 |
| 混合持久化 | 快 | 高(最多1秒) | 中 | 中 |
2.4 SLOWLOG 配置与慢查询分析
# redis.conf slowlog-log-slower-than 10000 # 记录执行时间超过 10ms 的命令(单位微秒) slowlog-max-len 1000 # 最多保留 1000 条慢日志
# 查看慢日志 redis-cli SLOWLOG GET 20 # 获取最近 20 条慢日志 redis-cli SLOWLOG LEN # 查看慢日志总数 redis-cli SLOWLOG RESET # 清空慢日志 # 慢日志输出格式: # 1) 1) (integer) 14 # 日志 ID # 2) (integer) 1706745600 # 执行时间戳 # 3) (integer) 15234 # 执行耗时(微秒) # 4) 1) "KEYS" # 命令 # 2) "*user*" # 5) "127.0.0.1:52341" # 客户端地址 # 6) "" # 客户端名称
三、示例代码和配置
3.1 完整配置示例
3.1.1 生产环境 redis.conf
# 文件路径:/etc/redis/redis.conf # 网络 bind 0.0.0.0 port 6379 protected-mode yes tcp-backlog 511 timeout 300 tcp-keepalive 300 # 内存管理 maxmemory 8gb maxmemory-policy allkeys-lru maxmemory-samples 10 # LRU/LFU 采样数,越大越精准但越耗 CPU 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 # 持久化(混合模式) appendonly yes appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite yes auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-use-rdb-preamble yes save 900 1 save 300 10 save 60 10000 rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir /var/lib/redis # 慢日志 slowlog-log-slower-than 10000 slowlog-max-len 1000 # 延迟监控 latency-monitor-threshold 100 # 记录超过 100ms 的延迟事件 # 线程(Redis 6.0+ I/O 多线程) io-threads 4 # I/O 线程数,建议为 CPU 核数的一半 io-threads-do-reads yes # 客户端 maxclients 10000
3.2 大 Key 检测与处理
3.2.1 检测大 Key
# 方法一:redis-cli 内置扫描(推荐,不阻塞) redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.1 # -i 0.1 表示每次扫描后休眠 0.1 秒,降低对生产的影响 # 方法二:使用 SCAN 替代 KEYS(生产环境禁止使用 KEYS *) redis-cli -h 127.0.0.1 -p 6379 --scan --pattern "user:*" | head -100 # 方法三:RDB 离线分析(最全面,零影响) # 使用 rdb-tools 分析 dump.rdb pip install rdbtools python-lzf rdb --command memory /var/lib/redis/dump.rdb | sort -t',' -k4 -rn | head -20
3.2.2 大 Key 处理策略
# 对于大 Hash(字段数 > 5000):拆分为多个小 Hash # 原始:HSET user:1000 field1 val1 field2 val2 ... field10000 val10000 # 拆分:按 field hash 分桶 # user0, user1, ... user99 # 对于大 List/Set(元素数 > 10000):分页存储或使用 Stream 替代 # 删除大 Key:使用 UNLINK 替代 DEL(异步删除,不阻塞主线程) redis-cli -h 127.0.0.1 UNLINK big_key_name # 批量异步删除 redis-cli -h 127.0.0.1 --scan --pattern "temp:*" | xargs redis-cli UNLINK
3.3 Pipeline 与 Lua 脚本优化
3.3.1 Pipeline 减少网络往返
# Python 示例:Pipeline 批量操作
import redis
r = redis.Redis(host='127.0.0.1', port=6379)
# 不使用 Pipeline:N 次网络往返
for i in range(1000):
r.set(f"key:{i}", f"value:{i}")
# 使用 Pipeline:1 次网络往返
pipe = r.pipeline(transaction=False) # transaction=False 不使用 MULTI/EXEC,性能更高
for i in range(1000):
pipe.set(f"key:{i}", f"value:{i}")
pipe.execute()
3.3.2 Lua 脚本保证原子性
-- 原子性 CAS(Compare And Swap)操作
-- 文件:cas.lua
local current = redis.call('GET', KEYS[1])
if current == ARGV[1] then
redis.call('SET', KEYS[1], ARGV[2])
return 1
else
return 0
end
# 执行 Lua 脚本 redis-cli EVAL "$(cat cas.lua)" 1 mykey old_value new_value # 生产环境推荐使用 SCRIPT LOAD + EVALSHA,避免每次传输脚本内容 redis-cli SCRIPT LOAD "$(cat cas.lua)" # 返回 SHA1,后续使用 EVALSHA sha1 1 mykey old_value new_value
四、最佳实践和注意事项
4.1 最佳实践
4.1.1 性能优化
禁用 THP(透明大页):THP 会导致 fork 时 COW 开销剧增,Redis 启动时会警告
# 临时禁用 echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag # 永久禁用(写入 /etc/rc.local 或 systemd service) cat >> /etc/rc.local << 'EOF' echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag EOF
调整 vm.overcommit_memory:避免 fork 时因内存不足失败
sysctl vm.overcommit_memory=1 echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf
连接池配置:避免频繁建立 TCP 连接
# Python redis-py 连接池 pool = redis.ConnectionPool( host='127.0.0.1', port=6379, max_connections=50, # 最大连接数 socket_timeout=1.0, # 命令超时 1 秒 socket_connect_timeout=1.0 ) r = redis.Redis(connection_pool=pool)
4.1.2 安全加固
启用 ACL 访问控制(Redis 6.0+):
# redis.conf aclfile /etc/redis/users.acl # users.acl 内容 user default off # 禁用默认用户 user appuser on >StrongPassword123 ~app:* +@read +@write +DEL # 应用用户 user monitor on >MonitorPass ~* +INFO +MONITOR # 监控用户
禁用危险命令:
# redis.conf rename-command KEYS "" # 禁用 KEYS rename-command FLUSHALL "FLUSHALL_DISABLED_9x8k2" rename-command FLUSHDB "FLUSHDB_DISABLED_7m3n1" rename-command CONFIG "CONFIG_9a2b3c" rename-command DEBUG ""
4.1.3 高可用配置
Sentinel 最小配置(3节点):
# sentinel.conf sentinel monitor mymaster 192.168.1.10 6379 2 # 需要 2 个 sentinel 同意才切换 sentinel down-after-milliseconds mymaster 5000 # 5秒无响应判定下线 sentinel failover-timeout mymaster 60000 # 故障转移超时 60 秒 sentinel parallel-syncs mymaster 1 # 同时只有 1 个 slave 同步新 master
4.2 注意事项
4.2.1 配置注意事项
警告:appendfsync always 在高写入场景下会导致 Redis 吞吐量下降 90% 以上,除非业务对数据零丢失有强制要求,否则不要使用。
KEYS * 命令在生产环境会阻塞 Redis 数秒,100万 Key 的实例执行 KEYS 约需 300ms,必须用 SCAN 替代
maxmemory 不设置时 Redis 会无限使用内存直到 OOM,容器环境尤其危险
AOF rewrite 期间磁盘写入量是平时的 2-3 倍,需要预留足够磁盘空间
4.2.2 常见错误
| 错误现象 | 原因分析 | 解决方案 |
|---|---|---|
| MISCONF Redis is configured to save RDB snapshots | 磁盘满或权限问题导致 RDB 保存失败 | 检查磁盘空间和 dir 目录权限 |
| OOM command not allowed when used memory > maxmemory | 内存达到上限且淘汰策略为 noeviction | 调整 maxmemory 或更换淘汰策略 |
| LOADING Redis is loading the dataset in memory | 重启后正在加载 AOF/RDB | 等待加载完成,大文件可能需要数分钟 |
| 延迟突增(latency spike) | fork 触发 COW,或 AOF rewrite | 检查 INFO stats 中 latest_fork_usec,考虑减少 save 频率 |
| 内存碎片率 > 2.0 | 大量 Key 过期后内存未释放 | 开启 activedefrag 或重启实例 |
五、故障排查和监控
5.1 故障排查
5.1.1 日志查看
# 查看 Redis 日志 tail -f /var/log/redis/redis-server.log # 查看延迟事件 redis-cli -h 127.0.0.1 LATENCY HISTORY event redis-cli -h 127.0.0.1 LATENCY LATEST redis-cli -h 127.0.0.1 LATENCY RESET # 实时监控命令流(慎用,高并发下会产生大量输出) redis-cli -h 127.0.0.1 MONITOR | head -100
5.1.2 常见问题排查
问题一:Redis 响应延迟突增
# 检查是否有慢查询 redis-cli SLOWLOG GET 10 # 检查 fork 耗时 redis-cli INFO stats | grep latest_fork_usec # 检查是否有阻塞命令 redis-cli INFO clients | grep blocked_clients # 检查内存使用 redis-cli INFO memory | grep -E "used_memory_human|mem_fragmentation_ratio"
问题二:内存持续增长不释放
# 检查 Key 数量和过期情况 redis-cli INFO keyspace # 检查内存分配详情 redis-cli MEMORY DOCTOR # 分析内存使用 redis-cli MEMORY USAGE keyname # 查看单个 Key 内存占用
问题三:AOF 文件异常增大
# 手动触发 AOF rewrite redis-cli BGREWRITEAOF # 检查 rewrite 状态 redis-cli INFO persistence | grep aof_rewrite
5.2 性能监控
5.2.1 关键指标监控
# 综合状态检查脚本 #!/bin/bash HOST="127.0.0.1" PORT="6379" echo "=== Redis 性能快照 ===" redis-cli -h $HOST -p $PORT INFO all | grep -E "redis_version|uptime_in_days|connected_clients|blocked_clients| used_memory_human|mem_fragmentation_ratio|total_commands_processed| instantaneous_ops_per_sec|rejected_connections|keyspace_hits| keyspace_misses|expired_keys|evicted_keys|latest_fork_usec| aof_enabled|rdb_last_bgsave_status"
5.2.2 监控指标说明
| 指标名称 | 正常范围 | 告警阈值 | 说明 |
|---|---|---|---|
| mem_fragmentation_ratio | 1.0~1.5 | > 2.0 | 内存碎片率 |
| blocked_clients | 0 | > 10 | 被阻塞的客户端数 |
| keyspace_hit_rate | > 90% | < 80% | 缓存命中率 |
| latest_fork_usec | < 200ms | > 1000ms | 最近一次 fork 耗时 |
| rejected_connections | 0 | > 0 | 被拒绝的连接数(达到 maxclients) |
| evicted_keys | 视业务 | 持续增长 | 被淘汰的 Key 数,持续增长说明内存不足 |
5.2.3 Prometheus 监控规则
# redis_alerts.yml
groups:
-name:redis
rules:
-alert:RedisMemoryFragmentation
expr:redis_mem_fragmentation_ratio>2.0
for:5m
labels:
severity:warning
annotations:
summary:"Redis 内存碎片率过高: {{ $value }}"
-alert:RedisHighMemoryUsage
expr:redis_memory_used_bytes/redis_memory_max_bytes>0.9
for:2m
labels:
severity:critical
annotations:
summary:"Redis 内存使用率超过 90%"
-alert:RedisCacheHitRateLow
expr:|
rate(redis_keyspace_hits_total[5m]) /
(rate(redis_keyspace_hits_total[5m]) + rate(redis_keyspace_misses_total[5m])) < 0.8
for:10m
labels:
severity:warning
annotations:
summary:"Redis 缓存命中率低于 80%: {{ $value | humanizePercentage }}"
5.3 备份与恢复
5.3.1 备份脚本
#!/bin/bash
# 文件名:redis_backup.sh
# 功能:Redis RDB 备份,保留 7 天
REDIS_CLI="/usr/bin/redis-cli"
BACKUP_DIR="/data/backup/redis"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7
mkdir -p "$BACKUP_DIR"
# 触发 BGSAVE
$REDIS_CLI -h 127.0.0.1 -p 6379 BGSAVE
# 等待 BGSAVE 完成
while [ "$($REDIS_CLI -h 127.0.0.1 INFO persistence | grep rdb_bgsave_in_progress | awk -F: '{print $2}' | tr -d '
')" = "1" ]; do
sleep 1
done
# 复制 RDB 文件
cp /var/lib/redis/dump.rdb "$BACKUP_DIR/dump_${DATE}.rdb"
# 压缩
gzip "$BACKUP_DIR/dump_${DATE}.rdb"
# 清理旧备份
find "$BACKUP_DIR" -name "dump_*.rdb.gz" -mtime +$RETENTION_DAYS -delete
echo"备份完成:$BACKUP_DIR/dump_${DATE}.rdb.gz"
5.3.2 恢复流程
停止服务:systemctl stop redis
恢复数据:cp dump_20260101_120000.rdb.gz /var/lib/redis/ && gunzip dump_*.rdb.gz && mv dump_*.rdb dump.rdb
验证完整性:redis-check-rdb /var/lib/redis/dump.rdb
重启服务:systemctl start redis && redis-cli PING
六、总结
6.1 技术要点回顾
内存碎片:mem_fragmentation_ratio > 1.5 时开启 activedefrag,禁用 THP 是前提
淘汰策略:纯缓存用 allkeys-lru,热点明显用 allkeys-lfu,混合存储用 volatile-lru
持久化:生产环境首选混合持久化(aof-use-rdb-preamble yes),兼顾恢复速度和数据安全
慢查询:KEYS * 是最常见的慢查询来源,用 SCAN 替代;大 Key 删除用 UNLINK
6.2 进阶学习方向
Redis Cluster 分片:水平扩展方案,16384 个 slot 的分配策略和 resharding 操作
实践建议:先在测试环境模拟节点故障,理解 cluster-node-timeout 对业务的影响
Redis Stream:替代 List 实现消息队列,支持消费者组和消息确认
实践建议:对比 Kafka 的适用场景,Stream 适合轻量级、低延迟的消息场景
Redis Function(Redis 7.0+):替代 Lua 脚本的新方案,支持库管理和持久化
实践建议:评估是否值得从 EVALSHA 迁移到 Function
6.3 参考资料
Redis 官方文档 - 配置参数权威参考
Redis 内存优化指南 - 官方内存优化建议
redis-rdb-tools - RDB 离线分析工具
附录
A. 命令速查表
redis-cli INFO memory # 内存使用详情 redis-cli INFO stats # 统计信息(命中率、连接数等) redis-cli INFO persistence # 持久化状态 redis-cli SLOWLOG GET 20 # 最近 20 条慢日志 redis-cli LATENCY LATEST # 最新延迟事件 redis-cli MEMORY DOCTOR # 内存诊断建议 redis-cli --bigkeys -i 0.1 # 扫描大 Key(带限速) redis-cli --scan --pattern "key:*" # 安全扫描 Key redis-cli BGSAVE # 触发后台 RDB 保存 redis-cli BGREWRITEAOF # 触发 AOF rewrite redis-cli DEBUG SLEEP 0 # 测试延迟基线 redis-cli CLIENT LIST # 查看所有客户端连接
B. 配置参数详解
| 参数 | 默认值 | 推荐值 | 说明 |
|---|---|---|---|
| maxmemory-samples | 5 | 10 | LRU/LFU 采样精度,越大越准但越耗 CPU |
| hz | 10 | 15~20 | 后台任务频率,影响过期 Key 清理速度 |
| dynamic-hz | yes | yes | 根据客户端数量动态调整 hz |
| lazyfree-lazy-eviction | no | yes | 异步淘汰 Key,避免阻塞 |
| lazyfree-lazy-expire | no | yes | 异步删除过期 Key |
| lazyfree-lazy-server-del | no | yes | DEL 命令异步执行 |
C. 术语表
| 术语 | 英文 | 解释 |
|---|---|---|
| 内存碎片率 | mem_fragmentation_ratio | RSS 与实际使用内存的比值,反映内存利用效率 |
| 写时复制 | Copy-On-Write (COW) | fork 后父子进程共享内存页,修改时才复制,影响 RDB/AOF rewrite 性能 |
| 淘汰策略 | Eviction Policy | 内存达到 maxmemory 时决定删除哪些 Key 的算法 |
| 慢日志 | Slow Log | 记录执行时间超过阈值的命令,用于性能分析 |
| 管道 | Pipeline | 批量发送命令减少网络往返次数的技术 |
全部0条评论
快来发表一下你的评论吧 !