Redis 分别提供了 RDB 和 AOF 两种持久化模式。
在 Redis 运行时,RDB 程序将当前内存中的数据库快照保存到磁盘文件中,在 Redis 重启动
时,RDB 程序可以通过载入 RDB 文件来还原数据库的状态。
RDB功能最核心的是rdbSave 和rdbLoad 两个函数,前者用于生成RDB文件到磁盘,
而后者则用于将RDB文件中的数据重载内存中。
保存
rdbSave函数负责内存中的数据以RDB格式保存到磁盘,
如果RDB文件已经存在,则替换现有文件。
在保存RDB文件期间,主进程会被阻塞,直到保存完成为止
save和bgsave调用方式区别
- SAVE 直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。
- 在主进程阻塞期间,服务器不能处理客户端的任何请求
- BGSAVE 则 fork 出一个子进程,子进程负责调用 rdbSave ,并在保存完成之后向主进程发送信号,通知保存已完成。
- 因为 rdbSave 在子进程被调用,所以 Redis 服务器在BGSAVE 执行期间仍然可以继续处理客户端的请求
伪代码
def SAVE(): rdbSave() def BGSAVE(): pid = fork() if pid == 0: # 子进程保存 RDB rdbSave() elif pid > 0: # 父进程继续处理请求,并等待子进程的完成信号 handle_request() else: # pid == -1 # 处理 fork 错误 handle_fork_error()
SAVE
- 当 SAVE 执行时,Redis 服务器是阻塞的,
- 所以当SAVE正在执行时,新的SAVE、BGSAVE或BGREWRITEAOF调用都不会产生任何作用。
- 因为 AOF 写入由后台线程完成,而 BGREWRITEAOF 则由子进程完成
- 所以在 SAVE执行的过程中,AOF 写入和 BGREWRITEAOF 可以同时进行
- 只是新的AOF 不产生作用,原来已存在
BGSAVE
- 在执行 SAVE 命令之前,服务器会检查 BGSAVE 是否正在执行当中,
- 如果是的话,服务器就不调用rdbSave,而是向客户端返回一个出错信息,告知在BGSAVE执行期间,不能行SAVE。
- 避免 SAVE 和 BGSAVE 调用的两个 rdbSave 交叉执行,造成竞争条件
- 当 BGSAVE 正在执行时,调用新 BGSAVE 命令的客户端会收到一个出错信息,告知 BGSAVE 已经在执行当中。
- BGREWRITEAOF 和 BGSAVE 不能同时执行
- 两个命令并没有什么冲突的地方不能同时执行它们只是一个性能方面的考虑
载入
- 当 Redis 服务器启动时,rdbLoad 函数就会被执行,它读取 RDB 文件,并将文件中的数据库数据载入到内存中。
- 在载入期间,服务器每载入 1000 个键就处理一次所有已到达的请求,
- 不过只有PUBLISH、SUBSCRIBE 、 PSUBSCRIBE 、 UNSUBSCRIBE 、 PUNSUBSCRIBE 会被正确地处理,
- 发布与订阅功能和其他数据库功能是完全隔离的
- 所以在服务器载入期间,订阅与发布功能仍然可以正常使用
- 其他命令一律返回错误。
- 不过只有PUBLISH、SUBSCRIBE 、 PSUBSCRIBE 、 UNSUBSCRIBE 、 PUNSUBSCRIBE 会被正确地处理,
- 因为 AOF 文件的保存频率通常要高于 RDB 文件保存的频率,一般AOF文件中的数据会比 RDB 文件中的数据要新
- 因此,如果服务器在启动时,打开了 AOF 功能,那么程序优先使用 AOF 文件来还原数据
- 只有在 AOF功能未打开的情况下,Redis 才会使用 RDB 文件来还原数据。
RDB文件结构
- REDIS
- 文件开头保存着redis五个字符,标识着一个RDB文件的开始
- RDB-VERSION
- 记录该文件所使用的RDB版本号
- 不同版本的RDB文件互不兼容,
- 程序读取时,需要根据不同版本号选择不同读入方式
- DB-DATA
- 这个部分在一个 RDB 文件中会出现任意多次
- 每个 DB-DATA 部分保存着服务器上一个非空数据库的所有数据。
- SELECT-DB
-
- 数据库号码
- KEY-VALUE-PAIRS
-
- 因为空的数据库不会被保存到 RDB 文件,所以这个部分至少会包含一个键值对的数据
- 每个键值对的数据使用以下结构来保存:
-
- OPTIONAL-EXPIRE-TIME | TYPE-OF-VALUE | KEY | VALUE
- OPTIONAL-EXPIRE-TIME
-
- OPTIONAL-EXPIRE-TIME域是可选的,如果键没有设置过期时间,那么这个域就不会出现
- 如果有,则是一个以毫秒为单位的Unix时间戳
- TYPE-OF-VALUE
-
- 记录着value域所使用的编码,根据这个编码,程序会使用不同的方式来保存和读取value值
- KEY
-
- 保存键值
- VALUE
- EOF
-
- 标志着数据库内容的结尾(不是文件的结尾)
- 值为 rdb.h/EDIS_RDB_OPCODE_EOF(255)
- CHECK-SUM
-
- RDB文件所有内容的校验和
- 如果为0 ,那么表示redis关闭校验和功能
- 这个部分在一个 RDB 文件中会出现任意多次
小结
- rdbSave 会将数据库数据保存到 RDB 文件,并在保存完成之前阻塞调用者
- SAVE 命令直接调用 rdbSave ,阻塞 Redis 主进程;BGSAVE 用子进程调用 rdbSave ,主进程仍可继续处理命令请求
- SAVE 执行期间,AOF 写入可以在后台线程进行,BGREWRITEAOF 可以在子进程进行,所以这三种操作可以同时进行
- 为了避免产生竞争条件,BGSAVE 执行时,SAVE 命令不能执行。
- 为了避免性能问题,BGSAVE 和 BGREWRITEAOF 不能同时执行
- 调用 rdbLoad 函数载入 RDB 文件时,不能进行任何和数据库相关的操作,不过订阅与发布方面的命令可以正常执行,因为它们和数据库不相关联。
- RDB 文件使用不同的格式来保存不同类型的值