Redis(二):RDB 持久化
文章目录
Redis 支持 RDB 和 AOF 两种持久化机制,持久化机制有效地避免了数据丢失问题,当重启时,根据之前的持久化文件即可实现数据的恢复。
1、RDB 持久化触发方式
RDB 持久化是把当前进程数据生成快照保存到硬盘的过程,有两种方式可以触发RDB 持久化,分别是 手动触发和自动触发
1.1、RDB 持久化手动触发
手动触发命令有两个,分别是 save 和 bgsave
1.1.1、save 命令
当 SAVE 命令执行时,Redis 服务器会被阻塞, 所以当 SAVE 命令正在执行时,客户端发送的 所有命令请求都会被拒绝。
save 命令执行时,会看到如下日志:
1:M 11 May 2019 04:26:19.927 * DB saved on disk
1.1.2、bgsave 命令
Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由该子进程负责完成。阻塞只发生在 fork 阶段,时间很短,在此期间会 block 父进程不能及时响应请求。
bgsave 命令执行时,会看到如下日志:
1:M 11 May 2019 04:27:07.734 * Background saving started by pid 23
23:C 11 May 2019 04:27:07.738 * DB saved on disk
23:C 11 May 2019 04:27:07.739 * RDB: 6 MB of memory used by copy-on-write
1:M 11 May 2019 04:27:07.818 * Background saving terminated with success
在 bgsave 命令执行期间,客户端发送的 sava 命令会被拒绝,服务其禁止 save 命令和 bgsave 命令同时执行,是为了避免父进程和子进程同时调用 rdbSave ,防止产生竞态条件。
Redis 内部涉及 RDB 的操作都采用 bgsave 方式,而 save 命令已经废弃
1.2、RDB 持久化自动触发
Redis 内部在某些场景下会自动触发 RDB 的持久化机制,如下:
1、配置文件中使用了 save 相关配置,eg:save m n 表示 m 秒内,修改了 n 次数据时,自动触发 bgsave。
2、集群情况下,从节点执行全量复制操作时,主节点会自动执行 bgsave 生成 RDB 文件并发送给从节点。
3、当执行 debug reload 命令重新加载 Redis 时,也会自动触发 save 操作。
4、默认情况下执行 shutdown 命令时,如果没有开启 AOF 持久化功能则会自动触发 bgsave
2、RDB 持久化流程
1、执行 bgsave 命令,Redis 父进程判断当前是否存在正在执行的子进程(RDB或AOF),如果存在 bgsave 命令直接返回。
2、父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,一般是毫秒级的,阻塞时间很短。可以通过 info stats 命令查看最近一次 fork操作的耗时(latest_fork_usec),单位是微秒
3、父进程fork完成后,bgsave 命令会返回“ Background saving started ”信息,然后就不再阻塞父进程,接着可以继续响应其它命令了。
4、子进程创建 RDB 文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子操作替换。可以通过 info stats 或 lastsave 命令查看最近一次生成 RDB 的时间(rdb_last_save_time)
5、子进程创建 RDB 文件后,会发送信号给父进程表示持久化完成,然后父进程更新统计信息
3、RDB 持久化文件
RDB 持久化机功能所生成的 RDB 文件是一个经过压缩的二进制文件,通过该文件可以还原数据库的状态。
因为 RDB 文件时保存在硬盘中的,所以即便 Redis 服务进程退出 或服务器停机,只要 RDB 文件存在,Redis 服务就可以用它来还原数据库状态。
3.1、RDB 文件的加载
RDB 文件的载入是在 Redis 服务启动时自动执行的,所以并没有专用于载入 RDB 文件的命令。只要 Redis 服务启动时检测到 RDB 文件存在,它就会自动载入 RDB 文件。
Redis 服务在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。
因为 AOF 文件的更新频率通常比 RDB 文件的更新频率高,所以持久化文件的加载会有以下情况值得注意:
- 如果Redis服务开启了AOF 持久化功能,会优先使用 AOF 文件还原数据库状态。
- 如果Redis服务关闭了AOF 持久化功能,则会使用 RDB 文件来还原数据库状态。
Redis 服务判断用哪个文件还原数据库状态的流程如下:
3.2、RDB 文件结构
一个完整的 RDB 文件会包含以下几个部分
redis | db_version | databases | eof | check_sum |
---|
redis:
RDB 文件的开头是 redis 部分, 这个部分的长度为5字节,保存着“ redis” 五个字符。 通过这五个字符,程序可以在载入文件时,快速检查所载入的文件是否 RDB 文件。
db_version:
db_version 长度为 4 字节,它的值是一个字符串表示的整数,这个整数记录了 RDB 文件的 版本号。
databases:
databases 部分包含零个或多个数据库,以及各个数据库中的键值对数据。
- 如果数据库为空,那么这个部分也为空,长度为0字节
- 如果数据库不为空,数据库所保存键值对的数量、类型和内容不同,这个部分的长度也会有所不同
eof:
EOF 常量的长度为 1 字节,这个常量标志着 RDB 文件正文内容的结束,当读入程序遇到这个 值的时候,它就认为所有数据库的所有键值对都已经载入完毕了。
check_sum:
check_sum 是一个长度为 8 字节的无符号整数,保存着一个校验和,这个校验和是根据前四个部分的内容进行计算得出的。Redis 服务在载入 RDB 文件时,会将载入数据所计算出的校验和与 check_sum 所记录的值进行比较,以此来检查 RDB 文件是否错误或者损坏。
4、RDB 持久化优缺点
4.1、RDB 的优点
1)RDB 是一个紧凑压缩的二进制文件,代表 Redis 服务在某个时间点的数据快照。非常适用于备份、全量复制等场景。
2)Redis 服务加载 RDB 文件速度远远优于 AOF 的方式
4.2、RDB 的缺点
1)RDB 方式 数据 没 办法 做到 实时 持久 化/ 秒 级 持久 化。 因为 bgsave 每次 运行
运行 都要 执行 fork 操作 创建 子 进程, 属于 重量级 操作, 频繁 执行 成本 过高。 ·RDB 文件 使用 特定 二进制 格式 保存, Redis 版本 演进 过程 中有 多个 格式 的 RDB 版本, 存在 老 版本 Redis 服务 无法 兼容 新版 RDB 格式 的 问题。 针对 RDB 不适合 实时 持久 化 的 问题, Redis 提供 了 AOF 持久 化 方式 来 解决。
2)RDB 文件使用二进制格式保存,Redis 版本演进过程中有多种格式的RDB 版本,因此存在老版本 Redis 无法兼容新版Redis 的RDB 格式的文件。针对 RDB 不适合实时持久化的问题,Redis 提供了 AOF 持久化方式来解决这个问题。
5、RDB 自动保存
我们可以通过设置配置文件中的 save 选项来配置多个保存条件,只要其中任意一个条件满足,Redis 服务就会执行 bgsave 命令。
举例说明,如下配置:
save 900 1
save 300 10
save 60 10000
只要满足三个条件中的任意一个,bgsave 命令就会被执行:
- 在 900 秒(15分钟)内,对数据库至少修改了 1 次
- 在 300 秒(5分钟)内,对数据库至少修改了 10 次
- 在 60 秒(1分钟)内,对数据库至少修改了 10000 次
5.1、RDB 自动保存原理
当 Redis 服务器 启动 时, 用户 可以 通过 指定 配置文件 或者 传入 启动 参数 的 方式 设置 save 选项, 如果 用户 没有 主动 设置 save 选项, 那么 服务器 会为 save 选项 设置 默认 条件:
当 Redis 服务器启动时,我们可以通过指定配置文件或传入启动参数来设置 save 选项,如果没有设置 save 选项,那么 Redis 服务会使用默认条件:
save 900 1
save 300 10
save 60 10000
然后 Redis 服务会根据 save 选项,设置服务状态 redisServer 的 saveparams 属性:
struct redisServer {
// ...
// 记录 save 条件的数组
struct saveparam *saveparams;
// ...
};
saveparams 属性是一个数组,数组中的每个元素都是一个saveparam 结构, 每个 saveparam 结构都保存了一个 save 选项设置的条件:
struct saveparam {
// 秒数
time_t seconds;
// 修改次数
int changes;
};
除了 saveparams 数组之外,redisServer 还维护着一个 dirty 计数器 和 lastsave 属性:
- dirty 计数器记录距离上一次成功执行 SAVE 命令 或者 BGSAVE 命令之后,服务器对数据库状态进行了多少次 修改(包括 写入、删除、更新等操作)。
- lastsave 属性是一个 UNIX 时间戳, 记录了服务器上一次成功执行 SAVE 或者 BGSAVE 命令的时间。
struct redisServer {
// ...
// 记录 save 条件的数组
struct saveparam *saveparams;
// 记录修改次数
long long dirty;
// 记录上一次持久化时间
time_t lastsave;
// ...
};
当成功执行一个数据库修改命令之后,程序就会对 dirty 计数器 和 lastsave 时间戳进行更新。
Redis 的服务器周期性操作函数 serverCron 默认 每隔 100 毫秒就会执行一次,该函数用于对正在运行的服务器进行维护,它的其中一项工作就是检查
save 选项所设置的条件是否已经满足,如果满足的 话,就执行 BGSAVE 命令。
上一篇: Java 复制PPT幻灯片