Skip to content

Redis的rdb快照记录时会堵塞吗

Redis 的 RDB(Redis Database)快照持久化机制,根据触发方式的不同,其阻塞行为也有所区别。主要有两种生成 RDB 快照的方式:SAVE 命令和 BGSAVE 命令。

1. SAVE 命令 (阻塞)

SAVE 命令会完全阻塞 Redis 服务器进程。

  • 同步执行:当执行 SAVE 命令时,Redis 主线程会同步地将所有数据写入 RDB 文件到磁盘。
  • 停止服务:在这个过程中,Redis 服务器无法处理任何其他命令请求,包括来自客户端的读写请求。
  • 生产环境不建议使用:由于 SAVE 命令会长时间阻塞 Redis,特别是在数据量较大时,会导致服务长时间不可用,因此在生产环境中几乎不使用 SAVE 命令,甚至已经基本被废弃。

2. BGSAVE 命令 (非阻塞,但 fork 阶段有短暂阻塞)

BGSAVE (Background Save) 命令是 Redis 默认和推荐的 RDB 快照方式,它旨在避免阻塞 Redis 主线程。

  • 创建子进程:当执行 BGSAVE 命令时,Redis 主进程会调用 fork() 系统调用,创建一个子进程。这个子进程专门负责将数据写入 RDB 文件,而父进程(Redis 主进程)则可以继续处理客户端的请求。
  • fork() 阶段的短暂阻塞:虽然 BGSAVE 命令本身是后台执行的,但 fork() 操作会短暂地阻塞 Redis 主进程。
    • fork() 操作会复制父进程的页表,这需要一定的时间和内存。
    • 阻塞时间的长短与 Redis 实例占用的内存大小成正比。例如,每 1GB 内存的 fork 操作可能耗时约 20 毫秒。 对于内存较大的实例,fork 阻塞时间可能比较明显。 可以通过 INFO stats 命令查看 latest_fork_usec 选项来获取最近一次 fork 操作的耗时(单位是微秒)。
  • 写时复制 (Copy-on-Write, COW)fork() 创建子进程后,父子进程会共享相同的内存页面。 只有当父进程(主 Redis 实例)或子进程(RDB 持久化进程)修改共享内存中的某个页面时,操作系统才会复制该页面,从而使得父子进程拥有各自独立的副本。
    • 这意味着子进程在保存 RDB 文件期间,父进程可以继续接收并处理写命令,而不会影响子进程正在快照的数据一致性。
    • COW 机制有效地减少了 fork 期间的内存开销,避免了完全复制整个数据集。
  • 内存开销:尽管有 COW 机制,但在 BGSAVE 期间,由于主进程的写操作会导致页面复制,Redis 的实际内存使用量可能会暂时增加,最高可能达到原有内存的 2 倍(极端情况下)。 如果系统可用内存不足,fork 操作可能会失败。