适用场景
本文适用于线上 Redis 开启 AOF 持久化后,业务在某些时间段出现接口变慢、写入超时、Redis used_cpu_sys 升高、磁盘 await 或 util 接近打满的场景。常见部署形态包括单机 Redis、主从 Redis、哨兵架构,以及运行在云主机本地盘或云盘上的 Redis 实例。
重点关注下面几类问题:
- AOF 文件持续增大,触发
BGREWRITEAOF后磁盘 IO 抖动。 - Redis fork 子进程期间内存页被频繁改写,产生明显的 copy-on-write 开销。
- AOF 重写期间业务写入仍然很高,导致 rewrite buffer 变大。
- 磁盘性能不足或与日志、备份、其他服务共用磁盘。
现象描述
一次线上告警中,业务接口 P99 延迟从 80ms 升到 1.5s 左右,少量请求报 Redis 写入超时。应用日志中可以看到类似信息:
redis command timeout: SETEX user:token:xxx 7200 ...
redis command timeout: HSET order:cache:xxx ...
Redis 自身没有重启,连接数也没有明显异常,但监控显示:
- Redis 写命令延迟周期性升高。
- 服务器磁盘
await从几毫秒升到几十甚至上百毫秒。 iostat中 Redis 所在磁盘%util接近 100%。- Redis 日志中出现
Background append only file rewriting started。
Redis 日志常见片段如下:
Background append only file rewriting started by pid 12834
Background AOF rewrite terminated with success
Residual parent diff successfully flushed to the rewritten AOF
Background AOF rewrite finished successfully
这些日志本身不代表故障,但如果它们与业务延迟、磁盘 IO 峰值在时间线上高度重合,就需要重点排查 AOF 重写。
可能原因
AOF 重写的目标是把当前数据集重新生成一份更紧凑的 AOF 文件,减少历史写命令堆积。它通常由 auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size 自动触发,也可以通过 BGREWRITEAOF 手动触发。
导致延迟升高的常见原因有:
- AOF 文件过大,重写耗时长。
- 磁盘吞吐和 IOPS 不足,重写顺序写与业务 AOF 追加写争抢磁盘。
- 写入流量在重写期间仍然很高,父进程需要维护 AOF rewrite buffer。
- Redis 进程内存较大,fork 后 copy-on-write 放大内存和 IO 压力。
appendfsync always或过于频繁的 fsync 策略让写入路径更容易被磁盘拖慢。- Redis 与日志采集、备份、数据库等服务共用同一块磁盘。
排查思路
排查时不要只看 Redis 是否存活,而要把 Redis 事件、慢请求、磁盘 IO 和系统资源放在同一条时间线上。
1. 查看 AOF 配置和当前状态
redis-cli -h 127.0.0.1 -p 6379 INFO persistence
重点看这些字段:
aof_enabled:1
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:38
aof_current_size:8589934592
aof_base_size:2147483648
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:12
latest_fork_usec:185000
关键字段解释:
aof_enabled:1表示当前开启 AOF。aof_rewrite_in_progress为 1 时,说明正在执行 AOF 重写。aof_current_size是当前 AOF 文件大小。aof_base_size是上次重写完成后的基准大小。aof_last_rewrite_time_sec可以判断上次重写是否异常耗时。aof_rewrite_buffer_length如果持续增长,说明重写期间新增写入较多。aof_delayed_fsync增长说明 fsync 被延迟,磁盘可能已经成为瓶颈。latest_fork_usec过高说明 fork 暂停时间明显,实例内存越大越容易放大。
如果 aof_current_size 远大于 aof_base_size,并且刚好达到自动重写阈值,就很可能是自动 rewrite 触发了抖动。
2. 查看 Redis 日志中的 rewrite 时间线
grep -E "AOF|rewrite|fork" /var/log/redis/redis-server.log
如果使用 systemd 管理 Redis,可以执行:
journalctl -u redis --since "2026-07-23 09:00:00" --until "2026-07-23 10:00:00" | grep -E "AOF|rewrite|fork"
需要关注三类时间:
- rewrite 开始时间。
- rewrite 完成时间。
- fork 耗时或失败日志。
如果业务延迟峰值刚好落在 rewrite 开始到结束之间,基本可以进入下一步验证。
3. 检查磁盘 IO 是否被打满
iostat -xz 1
重点看 Redis AOF 所在磁盘:
Device r/s w/s rkB/s wkB/s await aqu-sz %util
vdb 1.00 850.00 32.0 98000.0 78.50 12.30 99.20
字段判断方式:
w/s和wkB/s持续升高,说明写入压力大。await持续几十毫秒以上,说明 IO 请求排队或设备响应慢。aqu-sz增大说明磁盘队列堆积。%util接近 100% 时,单盘很可能已经满负荷。
如果 Redis 的 AOF 目录和业务日志目录在同一块盘,还要同步查看日志写入量,避免误判为 Redis 单独造成。
4. 找到 Redis AOF 文件所在磁盘
先查看 Redis 配置:
redis-cli CONFIG GET dir
redis-cli CONFIG GET appendfilename
示例输出:
1) "dir"
2) "/data/redis"
1) "appendfilename"
2) "appendonly.aof"
查看文件大小和挂载点:
ls -lh /data/redis/appendonly.aof
df -h /data/redis
findmnt /data/redis
如果 Redis 7 使用 multi-part AOF,还可以查看 AOF 目录:
redis-cli CONFIG GET appenddirname
ls -lh /data/redis/appendonlydir
multi-part AOF 下通常会看到 base、incremental 和 manifest 文件。不要手工删除这些文件,应通过 Redis 命令和配置管理。
5. 检查慢日志和延迟事件
redis-cli SLOWLOG GET 20
redis-cli LATENCY LATEST
如果未开启延迟监控,可以临时设置阈值:
redis-cli CONFIG SET latency-monitor-threshold 100
常见输出类似:
1) 1) "aof-write"
2) (integer) 1721701200
3) (integer) 320
4) (integer) 800
这里的 aof-write、aof-fsync-always、fork 等事件都值得关注。LATENCY DOCTOR 可以给出 Redis 视角的诊断建议:
redis-cli LATENCY DOCTOR
定位示例
某 Redis 实例配置如下:
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
事故发生时 INFO persistence 关键字段如下:
aof_rewrite_in_progress:1
aof_current_size:11.2G
aof_base_size:5.4G
aof_rewrite_buffer_length:268435456
aof_delayed_fsync:47
latest_fork_usec:243000
同时 iostat -xz 1 显示:
Device r/s w/s rkB/s wkB/s await aqu-sz %util
vdb 2.00 910.00 64.0 112000.0 96.30 18.70 99.80
结合 Redis 日志:
09:31:08 Background append only file rewriting started by pid 28144
09:33:46 Background AOF rewrite terminated with success
09:33:50 Background AOF rewrite finished successfully
业务延迟峰值集中在 09:31 到 09:34,说明 AOF 重写与磁盘 IO 打满是主要触发因素。这里不是 Redis 连接池问题,也不是网络抖动,而是持久化写入路径被慢盘拖住。
修复方案
1. 调整自动重写阈值,减少高峰期触发
如果 AOF 增长较快,默认阈值可能导致重写过于频繁。可以适当提高阈值:
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 200
auto-aof-rewrite-min-size 1gb
含义:
auto-aof-rewrite-percentage 200表示当前 AOF 大小达到上次重写后基准大小的 3 倍左右再触发。auto-aof-rewrite-min-size 1gb表示 AOF 至少超过 1GB 才考虑自动重写。
调整后需要结合磁盘容量评估,因为阈值提高会让 AOF 文件在两次重写之间变得更大。
2. 避开业务高峰手动重写
对写入高峰明显的业务,可以关闭自动重写或提高阈值,然后在低峰期主动执行:
redis-cli BGREWRITEAOF
执行前建议检查:
redis-cli INFO memory | egrep "used_memory_human|used_memory_rss_human|mem_fragmentation_ratio"
redis-cli INFO persistence | egrep "aof_current_size|aof_base_size|aof_rewrite_in_progress"
df -h /data/redis
iostat -xz 1
不要在磁盘空间紧张、实例内存很大且写入高峰时强行执行 BGREWRITEAOF。
3. 保持 appendfsync everysec,避免 always
多数业务建议使用:
appendfsync everysec
appendfsync always 每个写命令都尽量落盘,延迟受磁盘影响非常明显,除非业务确实需要极强的持久化语义,否则不建议在线上高写入实例使用。
如果希望降低 rewrite 期间 fsync 对延迟的影响,可以评估:
no-appendfsync-on-rewrite yes
它的效果是 AOF 重写期间主进程不主动 fsync AOF 追加文件,通常可以降低延迟抖动,但代价是宕机时可能丢失更多最近写入。是否启用要由业务 RPO 要求决定。
4. 将 Redis 数据目录放到独立磁盘
如果 Redis 与应用日志、Nginx 日志、备份任务共用磁盘,AOF 重写很容易被其他顺序写放大。建议:
- Redis AOF/RDB 目录使用独立数据盘。
- 避免把备份压缩、日志归档任务放在 Redis 高峰期。
- 云盘场景关注 IOPS、吞吐上限和突发额度。
- 对关键实例使用性能更稳定的 SSD 或云盘高性能规格。
5. 降低单实例写入和数据集规模
如果单实例数据集过大,fork 和 copy-on-write 成本都会升高。可以考虑:
- 按业务维度拆分实例。
- 清理无 TTL 的临时缓存 key。
- 对大 hash、list、zset 做拆分。
- 降低无意义的重复写入。
- 对缓存类数据设置合理过期时间。
检查无 TTL key 的示例脚本:
redis-cli --scan --pattern 'cache:*' | while read key; do
ttl=$(redis-cli TTL "$key")
if [ "$ttl" -eq -1 ]; then
echo "$key"
fi
done
这个脚本适合小规模抽查。大实例不要在高峰期全量扫描所有 key,可以按 pattern 分批执行,并控制执行频率。
预防措施
建议为 Redis AOF 建立固定监控项:
aof_current_size:当前 AOF 文件大小。aof_base_size:上次重写后的基准大小。aof_rewrite_in_progress:是否正在重写。aof_last_rewrite_time_sec:上次重写耗时。aof_delayed_fsync:fsync 延迟次数。latest_fork_usec:最近一次 fork 耗时。- 磁盘
await、%util、剩余空间。 - Redis 命令延迟和业务接口 P95/P99。
Prometheus redis_exporter 中可以重点关注类似指标:
redis_aof_current_size_bytes
redis_aof_base_size_bytes
redis_aof_rewrite_in_progress
redis_aof_delayed_fsync_total
redis_latest_fork_seconds
告警思路示例:
redis_aof_rewrite_in_progress == 1
increase(redis_aof_delayed_fsync_total[5m]) > 0
redis_aof_current_size_bytes / redis_aof_base_size_bytes > 3
这些告警不一定都要直接通知业务值班,但至少应该进入 Redis 运行健康看板,方便发生延迟时快速关联。
注意事项
- AOF 重写不是删除数据,它只是把现有数据重新编码成更紧凑的持久化文件。
- 不要手工删除线上 AOF 文件,除非已经明确知道恢复后果并完成备份。
BGREWRITEAOF是后台任务,但 fork 阶段仍可能短暂停顿。- 大实例执行 rewrite 前要确认内存余量,copy-on-write 可能让 RSS 明显增长。
- 磁盘空间要预留足够余量,因为重写期间新旧 AOF 文件可能同时存在。
- 如果 Redis 只做可丢缓存,可以重新评估是否必须开启 AOF。
总结
Redis AOF 重写导致的延迟问题,本质上通常不是 Redis 命令本身变慢,而是持久化、fork、copy-on-write 和磁盘 IO 共同造成的抖动。排查时应先用 INFO persistence 判断 rewrite 状态,再用 Redis 日志确认时间线,最后结合 iostat、慢日志和延迟监控定位瓶颈。
治理上不要只依赖一次手动重写。更稳妥的做法是合理设置自动重写阈值,避开业务高峰,保持磁盘隔离和充足空间,持续监控 AOF 大小、fsync 延迟与 fork 耗时。这样才能在保留持久化能力的同时,把 Redis 写入延迟控制在可接受范围内。
Discussion
评论