持久化卡顿从哪来
Redis 用单线程处理命令,凡是 fork、AOF 重写、磁盘慢这类系统级动作,都会把延迟传导到客户端,表现为周期性卡顿或超时毛刺。排查的顺序是固定模板:先确认是 fork 慢、重写慢还是磁盘慢,再对症处理,而不是盲调内存参数。
基本信息
| 关键指标 | latest_fork_usec、rdb_bgsave_in_progress、aof_rewrite_in_progress、last_bgsave_status |
|---|---|
| 延迟工具 | LATENCY LATEST / HISTORY,需要先设置 latency-monitor-threshold |
| 系统层工具 | iostat -x、pidstat -d、iotop |
| 常见放大因素 | 大 key 删除与集中过期、THP 开启、单实例内存过大 |
| 关联配置 | stop-writes-on-bgsave-error、save 条件、auto-aof-rewrite 阈值 |
典型使用场景
- 应用侧报 Redis 超时,但实例 CPU 与 QPS 都不高
- 每天固定时间点出现延迟毛刺,怀疑快照或重写
- 上次快照失败后实例拒绝写入,出现 MISCONF 类报错
- 同机多实例互相抢磁盘 IO,需要定位谁在压盘
- 扩容或迁移后延迟反而变差,核对新盘的性能与挂载参数
排查路径
排查路径
INFO stats | grep latest_fork_usec # 最近一次 fork 耗时(微秒)
INFO persistence | grep -E "in_progress|last_bgsave_status|last_write_status"
LATENCY LATEST # 各事件最新延迟(需先开启采集)
LATENCY HISTORY fork # fork 事件的历史曲线
CONFIG GET latency-monitor-threshold
LATENCY DOCTOR # 汇总成一句话结论
iostat -x 1 5 # 盘利用率与 await
pidstat -d -p $(pgrep -f redis-server) 1 5 # 进程级读写速率现象与处置方向
| 现象 | 可能原因 | 处置方向 |
|---|---|---|
| latest_fork_usec 很大 | 数据集大、写时复制页多、THP 开启 | 关闭 THP,拆分实例,控制单实例内存 |
| bgsave 长时间 in_progress | 磁盘慢或数据集过大 | 换更高 IOPS 的盘,错开快照时间 |
| aof_rewrite_in_progress 长期为 1 | 重写阈值偏小或盘慢 | 调大 auto-aof-rewrite 阈值,评估盘性能 |
| last_bgsave_status 为 err | 落盘目录无权限或磁盘满 | 修目录权限与属主,扩容或清理 |
| 延迟集中在 expire 事件 | 大量键在同一秒过期 | 过期时间加随机抖动,分散到时间轴 |
排查前先把采集打开:latency-monitor-threshold 为 0 时 LATENCY 系列命令永远为空,事后无法复盘到底是 fork 还是磁盘导致,只能靠猜。
大 key 的删除与过期同样会卡住单线程,别把锅全推给持久化。配合慢日志与 --bigkeys(在低峰或副本上执行)一起看,往往能找到真正的时间点重合。
把快照安排在业务高峰,等于把磁盘 IO 峰值叠加在用户请求上。维护窗口或从库上执行,并确认 stop-writes-on-bgsave-error 的拒绝写后果是否符合预期。