首页/数据/Redis 进阶速查/fork 与写时复制(COW)机制
持久化56 条

fork 与写时复制(COW)机制

fork 瞬间内核复制父进程页表,父子共享物理页;子进程写数据时内核按页复制新副本,父进程继续写自己的新页,旧值保留给快照读取。

Redis 进阶速查数据更新于 2026-10-13

fork 与写时复制

快照与 AOF 重写都依赖同一个操作系统能力:fork。调用瞬间内核为子进程复制一份父进程的页表,父子进程随后共享同一批物理内存页,这一过程本身非常快。等到任意一方要修改某个页面时,内核先把该页复制出一份新副本给对方,父子各自写自己的副本,这就是写时复制(COW)。子进程因此能在「时间静止」的视图上遍历数据,父进程可以继续接收写入。

基本信息

复制粒度内存页,通常 4KB,与数据内容无关
fork 开销页表拷贝,与实例内存量近似线性
观测指标INFO stats 的 latest_fork_usec,单位为微秒
内存影响快照期间写入越多,被复制的页越多,RSS 上升越明显
相关机制透明大页(THP)会放大 fork 与 COW 的开销

典型使用场景

  • 容量规划:按实例内存估算 fork 停顿与快照期间的额外内存占用,预留副本空间
  • 排查周期性延迟抖动:请求延迟每隔一段时间出现尖峰,往往是快照或重写的 fork 造成
  • 判断快照是否会触发 OOM:内存水位高时 fork 后的 COW 增长可能把实例推向淘汰或崩溃
  • 优化大实例的运维窗口:把快照与批量导入安排在低峰,避免写入放大 COW 复制量
  • 评估容器内存限制:容器内存上限必须覆盖数据加 COW 峰值,否则会被调度器直接杀掉

观测与配置

观测与配置
redis-cli INFO stats | grep latest_fork_usec          # fork 耗时,微秒
redis-cli INFO memory | grep -E 'used_memory_human|used_memory_rss_human'
redis-cli INFO persistence | grep rdb_bgsave_in_progress
cat /sys/kernel/mm/transparent_hugepage/enabled       # 建议为 madvise 或 never
sysctl vm.overcommit_memory                           # 大内存实例建议设为 1

指标读法

指标正常表现异常信号
latest_fork_usec与内存规模匹配,通常数毫秒到数十毫秒达到数十万微秒说明停顿严重
used_memory_rss略高于 used_memory快照期间大幅飙升,接近容器上限
rdb_bgsave_in_progress短暂为 1长期为 1 说明快照卡住
THP 状态madvise 或 neveralways 会显著放大 fork 与 COW 开销
为什么 fork 耗时与内存量相关:子进程需要一份完整的页表副本,内存越大页表层级与条目越多,拷贝越久。这也是大实例必须限制单实例内存规模的原因之一,把 100GB 拆成多个较小实例,单次 fork 的停顿会明显缩短。
COW 带来的内存增长无法精确预测,它取决于快照期间被修改的页面数量。在内存水位已经很高的实例上触发快照,可能瞬间超出物理内存导致 OOM 或被容器杀掉,快照窗口应避开批量写入并预留余量。

易错点

  • 忽略 fork 停顿只看平均延迟,周期性尖峰被当成网络抖动处理,实际原因是快照与重写在同一时段叠加
  • 开机默认开启透明大页且未调整,Linux 上单个页表项覆盖内存变大,fork 与 COW 开销被成倍放大
  • 容器内存上限按数据量设置而未给 COW 留余量,快照期间被 OOM 杀掉,表现为实例毫无征兆地重启并加载旧快照
  • 在写入高峰期执行快照或重写,被修改的页面激增,内存与磁盘 IO 同时吃紧,批量导入任务应避开快照窗口

数据来源与参考

  • Redis 官方文档:BGSAVE 与 fork 行为说明
  • Linux man 2 fork(写时复制)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌