MD 更新:未知

Redis bgsave 为什么不阻塞

关联笔记: Redis与缓存考点总结RDBAOF


核心答案

bgsave 使用 fork() 创建子进程来执行持久化,父进程(主线程)继续处理客户端请求,因此不阻塞。


save vs bgsave 对比

save(阻塞):
  主线程 → [执行save] → [写磁盘完成] → 继续处理请求
            ↑______________↑
            这段时间 Redis 完全卡死

bgsave(不阻塞):
  主线程 → fork() → 继续处理请求 ──────────────────→

  子进程  →        [写磁盘完成] → 通知父进程
对比项savebgsave
执行线程主线程子进程
是否阻塞阻塞不阻塞
适用场景不推荐生产环境推荐

bgsave 不阻塞的原理

机制:fork() + Copy-on-Write(写时复制)

第一步:fork() 创建子进程

  • Redis 调用 fork() 系统调用
  • 操作系统创建一个子进程
  • 子进程拥有父进程内存空间的逻辑副本

第二步:Copy-on-Write(写时复制)

关键点:fork 时子进程不会立即复制全部内存

fork 后的内存布局:

  父进程(主线程)        子进程
  ┌──────────┐          ┌──────────┐
  │ 内存页 A  │──共享──→ │ 内存页 A  │
  │ 内存页 B  │──共享──→ │ 内存页 B  │
  │ 内存页 C  │──共享──→ │ 内存页 C  │
  └──────────┘          └──────────┘

  当父进程修改内存页 B 时:

  ┌──────────┐          ┌──────────┐
  │ 内存页 A  │──共享──→ │ 内存页 A  │
  │ 内存页 B' │  复制    │ 内存页 B  │ ← 子进程仍读旧数据
  │ 内存页 C  │──共享──→ │ 内存页 C  │
  └──────────┘          └──────────┘
  • 只有当某个进程要修改某内存页时,OS 才会复制该页
  • 子进程读取的是 fork 那一刻的数据快照
  • 父进程继续正常服务,修改数据不影响子进程

第三步:各司其职

进程职责是否阻塞
父进程(主线程)继续处理客户端请求不阻塞
子进程将内存数据写入 RDB 文件不影响父进程

潜在的短暂阻塞点

fork() 调用本身会有一个极短暂的阻塞(微秒到毫秒级),用于:

  • 复制页表
  • 创建子进程控制块

例外情况: 如果 Redis 实例内存非常大(几十GB),fork 的时间会变长,可能出现短暂卡顿。


考试答题模板

问:为什么 bgsave 不阻塞?

答:

  1. bgsave 使用 fork() 创建子进程来执行持久化
  2. 子进程通过 Copy-on-Write(写时复制) 机制读取 fork 时刻的数据快照
  3. 父进程(主线程)继续处理客户端请求,不受影响
  4. 子进程完成写入后通知父进程

相关笔记