appendfsync always 仍会丢数据,因存在内核page cache缓存、硬盘写缓存未禁用、redis进程崩溃于write与fsync之间三重断层;everysec是默认推荐,兼顾可靠性与吞吐;no仅适用于纯缓存或高写压且有外部兜底的场景。

Redis AOF 的 appendfsync 不是“越快越好”,而是要根据数据安全边界和写入吞吐之间的实际权衡来选;设成 always 并不能真正保证不丢数据,反而可能让写性能跌到不可用的程度。
为什么 appendfsync always 依然会丢数据?
很多人以为 appendfsync always 表示每次 write() 后立刻 fsync() 到磁盘,就等于“绝对不丢”。但现实里有三个关键断层:
- Linux 内核的 page cache 层仍可能缓存
fsync()调用本身(尤其在某些 ext4 + barrier 关闭组合下) - 硬盘/SSD 自身的写缓存(Write Cache)若未禁用或未通过
hdparm -W0关闭,fsync()只是通知设备“数据已就绪”,不代表落盘 - Redis 进程崩溃前若刚完成
write()但还没走到fsync()(比如被 SIGKILL 强杀),AOF 缓冲区里那条命令就没了
所以 always 实际提供的是“最多丢失 1 条命令”的理论上限,前提是硬件、内核、Redis 三者都按理想路径协作——这在生产环境极少成立。
appendfsync everysec 是大多数场景的默认起点
它把 AOF 缓冲区每秒刷一次盘,兼顾了可靠性与吞吐。但要注意几个隐含行为:
- 不是严格“每 1000ms 一次”,而是靠 Redis 主循环中的定时器触发,若主线程卡住(如执行
KEYS *或大 value 序列化),刷盘可能延迟数秒 - 后台子进程重写 AOF(
bgrewriteaof)期间,主进程仍持续追加新命令到旧 AOF 文件,everysec对这部分依然生效 - 当磁盘 I/O 延迟突增(如 RAID 卡电池学习、SSD 垃圾回收),Redis 会临时把刷盘策略降级为
always直到恢复——这个自动降级行为由no-appendfsync-on-rewrite no控制,默认是no,即不跳过
如果你的应用能接受秒级 RPO(例如用户发帖失败重试成本低),everysec 就够用,且比 always 高出 5–10 倍写吞吐。
什么时候该考虑 appendfsync no?
no 表示完全交由操作系统决定刷盘时机(通常是 30–60 秒),只适合两类场景:
- 纯缓存型 Redis,AOF 仅用于故障后快速重建热 key 分布,不要求数据精确恢复(比如 session 存储 + 前端兜底)
- 写压极高、且磁盘 I/O 已是瓶颈的集群节点,比如每秒写入 50k+ 小命令,
everysec下磁盘 util 持续 >90%,此时开no能显著降低 write wait
但必须同步做两件事:关掉 save 配置项(避免 fork + rdb save 加剧延迟),并确保有外部机制补位——比如应用层双写 MySQL 或投递消息队列,否则宕机就是全量丢失。
AOF 重写期间的 sync 行为容易被忽略
很多人没意识到:bgrewriteaof 过程中,新命令仍在往老 AOF 文件写,而 appendfsync 设置对这部分写操作仍然生效。也就是说:
- 设的是
always→ 重写期间照样每条命令 fsync,I/O 压力翻倍 - 设的是
everysec→ 老文件刷盘节奏不变,但新生成的 AOF 文件是子进程一次性写完再原子替换,不走 sync 流程
如果重写频繁(比如 auto-aof-rewrite-percentage 设得太低),又用了 always,就会陷入“重写→卡主线程→刷盘更慢→更多积压→更频繁重写”的恶性循环。这时候要么调高重写阈值,要么换用 everysec。










