介绍 RDB

RDB 持久化是通过保存数据库中的键值对来记录数据库状态。
RDB 持久化既可以手动执行,也可以根据服务器参数配置项定期执行,并且正常停止 Redis 服务也会执行 RDB 持久化。RDB 持久化功能可以将某个时间点上的数据库状态保存到一个 RDB 文件中。
RDB 持久化功能所生成的 RDB 文件是一个经过压缩的二进制文件,通过该文件可以还原生成 RDB 文件时的数据库状态。

RDB 文件的创建:save、bgsave

有两个 Redis 命令可以用于生成 RDB 文件, 一个是 save, 另一个是 bgsave。

  • save 命令由服务器进程直接执行 RDB 文件的创建,所以该命令会阻塞 Redis 服务器进程, 直到 RDB 文件创建完成。在服务器进程阻塞期间, 服务器不能处理任何命令请求。
  • bgsave 命令会 fork 一个子进程,由子进程执行 RDB 文件的创建,所以 bgsave 命令不会阻塞服务器。服务器进程(父进程)可以继续处理命令请求

创建 RDB 文件的实际工作由 rdb.c/rdbSave 函数完成, save 命令和 bgsave 命令会以不同的方式调用这个函数,通过以下伪代码可以明显地看出这两个命令之间的区别:

  1. def SAVE():
  2. # 创建RDB文件
  3. rdbSave()
  4. def BGSAVE():
  5. # 创建子进程
  6. pid = fork()
  7. if pid == 0:
  8. # 子进程负责创建RDB文件
  9. rdbSave()
  10. # 完成之后向父进程发送信号
  11. signal_parent()
  12. elif pid > 0:
  13. #父进程继续处理命令请求, 并通过轮询等待子进程的信号
  14. handle_request_and_wait_signal()
  15. else:
  16. #处理出错情况
  17. handle_fork_error()

Redis 服务器自动创建 RDB 文件

Redis 允许用户通过设置服务器配置的 save 选项,让服务器每隔一段时间自动执行一次 bgsave 命令。
用户可以通过 save 选项设置多个保存条件,只要其中任意一个条件被满足,服务器就会执行 bgsave 命令。如果用户没有主动设置 save 选项, 那么服务器会为 save 选项设置默认条件:

  1. save 900 1
  2. save 300 10
  3. save 60 10000

上面的默认条件的意思是,只要满足以下三个条件中的任意一个,bgsave 命令就会被执行:

  • 服务器在 900 秒之内, 对数据库进行了至少 1 次修改。
  • 服务器在 300 秒之内, 对数据库进行了至少 10 次修改。
  • 服务器在 60 秒之内, 对数据库进行了至少 10000 次修改。

    用户可以通过指定配置文件或者传入启动参数的方式设置 save 选项。

RDB 文件的载入

RDB 文件的的载入工作是在服务器启动时自动执行的,所以 Redis 并没有专门用于载入 RDB 文件的命令, 只要 Redis 服务器在启动时检测到 RDB 文件存在,它就会自动载入 RDB 文件。
另外值得一提的是, 因为 AOF 文件的更新频率通常比 RDB 文件的更新频率高,所以:

  • 如果服务器开启了 AOF 持久化功能,那么服务器会优先使用 AOF 文件来还原数据库状态。
  • 只有在 AOF 持久化功能处于关闭状态时,服务器才会使用 RDB 文件来还原数据库状态。

载入 RDB 文件的实际工作由 rdb.c/rdbLoad 函数完成。

创建 / 载入 RDB 文件时,服务器的状态

创建 RDB 文件时,服务器的状态
当 save 命令执行时,Redis 服务器会被阻塞,所以当 save 命令正在执行时,客户端发送的所有命令请求都会被拒绝。只有在服务器执行完 save 命令、重新开始接受命令请求之后,客户端发送的命令才会被处理。
当 bgsave 命令执行时,因为 bgsave 命令的保存工作是由子进程执行的, 所以在子进程创建 RDB 文件的过程中, Redis 服务器仍然可以继续处理客户端的命令请求, 但是, 在 bgsave 命令执行期间, 服务器处理 save、bgsave、bgrewriteaof 这三个命令的方式会和平时有所不同。

  • 首先, 在 bgsave 命令执行期间, 客户端发送的 save 命令会被服务器拒绝, 服务器禁止 save 命令和 bgsave 命令同时执行是为了避免父进程(服务器进程) 和子进程同时执行两个 rdbSave 调用, 防止产生竞争条件。
  • 其次, 在 bgsave 命令执行期间, 客户端发送的 bgsave 命令会被服务器拒绝, 因为同时执行两个 bgsave 命令也会产生竞争条件。
  • 最后, bgrewriteaof 和 bgsave 两个命令不能同时执行:
    • 如果 bgsave 命令正在执行, 那么客户端发送的 bgrewriteaof 命令会被延迟到 bgsave 命令执行完毕之后执行。
    • 如果 bgrewriteaof 命令正在执行, 那么客户端发送的 bgsave 命令会被服务器拒绝。

      因为 bgrewriteaof 和 bgsave 两个命令的实际工作都由子进程执行, 所以这两个命令在操作方面并没有什么冲突的地方, 不能同时执行它们只是一个性能方面的考虑——两个子进程并发, 并且这两个子进程都同时执行大量的磁盘写入操作, 这怎么想都不会是一个好主意。


载入 RDB 文件时,服务器会一直处于阻塞状态,直到载入工作完成为止。

我的疑问

客户端发送的 save、bgsave 命令有可能会被服务器拒绝,那拒绝之后呢?是重试,还是放弃执行。

参考资料

《Redis 设计与实现》
05 | 内存快照:宕机后,Redis如何实现快速恢复?
18 | 如何生成和解读RDB文件?