Redis中的RDB持久化
说明
本文是学习黄健宏《Redis设计与实现》关于RDB持久化中,阅读源码之后的一点心得体会。
该书成文于2014年,使用的版本是2.9,笔者使用的是6.0.6,其中会有差异,可能概念的描述会有所过期,还望可以留言指正。
定义
我们定义“数据库状态”如下:
某一刻服务器中的非空数据库以及其中的键值对。
数据库状态相当于某一时刻redis内存中需要持久化数据的快照。
将数据库状态完整保存到磁盘的方式叫做RDB持久化。
rdb文件的生成
RDB持久化的时间点有两种方式指定
- 用户手动调用save或者bgsave
- redis程序定期调用
1.手动调用生成:
手动调用save: redis服务器接收到指令后,会开始将数据写入到RDB文件,同时阻塞外界一切请求,是一个阻塞类型的调用。 bgsave: redis收到指令后,会fork一个子进程出来,子进程中将数据写入到rbd文件,这时父进程还可以处理外界请求。
但是在执行bgsave期间,收到save或者bgsave会直接拒绝返回错误,收到bgrewriteaof,此命令不会拒绝,但不会同时执行,bgrewriteaof的过程会在bgsave结束后调用,bgrewriteaof用于生成aof持久化文件。
2.自动保存:
可以在配置文件中这样设置
save <seconds> <changes>
表示在seconds的范围时间内,至少changes次操作以后,会触发一下同步。
默认为
save 900 1
save 300 10
save 60 10000
表示在900秒内一次|300秒内10次|60秒内10000次
自动保存的实现细节:
struct redisServer {
// ...
long long dirty; /* Changes to DB from the last save */
struct saveparam *saveparams; /* Save points array for RDB */
int saveparamslen; /* Number of saving points */
time_t lastsave; /* Unix time of last successful save */
};
struct saveparam {
time_t seconds;
int changes;
};
- lastsave记录上一次rdb成功保存的时间
- dirty记录上一次成功保存之后,数据库中的数据经历过多少次变化
- saveparams记录了服务端关于自动保存的参数配置
redis服务器默认每100毫秒执行一次定时任务,下述为定时任务中的一段代码逻辑
for (j = 0; j < server.saveparamslen; j++) {
struct saveparam *sp = server.saveparams+j;
/* Save if we reached the given amount of changes,
* the given amount of seconds, and if the latest bgsave was
* successful or if, in case of an error, at least
* CONFIG_BGSAVE_RETRY_DELAY seconds already elapsed. */
if (server.dirty >= sp->changes &&
server.unixtime-server.lastsave > sp->seconds &&
(server.unixtime-server.lastbgsave_try >
CONFIG_BGSAVE_RETRY_DELAY ||
server.lastbgsave_status == C_OK))
{
serverLog(LL_NOTICE,"%d changes in %d seconds. Saving...",
sp->changes, (int)sp->seconds);
rdbSaveInfo rsi, *rsiptr;
rsiptr = rdbPopulateSaveInfo(&rsi);
rdbSaveBackground(server.rdb_filename,rsiptr);
break;
}
}
可以看到,每次会取出保存的参数进行对比,成立的话会在fork一个子进程出来进行同步。
写入时会先写一个temp文件,然后再进行rename操作
temp文件格式如下:
snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
RDB文件的格式:
RDB文件是一个按照固定格式存储的二进制数据,具体格式本文不做介绍。
可以使用od -cx dump.rdb
来打印可视化的rdb文件
zhengshi@bj-dl-003:~/software/redis/data$ od -cx dump.rdb
0000000 R E D I S 0 0 0 9 372 \t r e d i s
4552 4944 3053 3030 fa39 7209 6465 7369
0000020 - v e r 005 6 . 0 . 6 372 \n r e d i
762d 7265 3605 302e 362e 0afa 6572 6964
0000040 s - b i t s 300 @ 372 005 c t i m e 302
2d73 6962 7374 40c0 05fa 7463 6d69 c265
0000060 020 } q e 372 \b u s e d - m e m 302 310
7d10 6571 08fa 7375 6465 6d2d 6d65 c8c2
0000100 A \f \0 372 \f a o f - p r e a m b l
0c41 fa00 610c 666f 702d 6572 6d61 6c62
0000120 e 300 \0 376 \0 373 001 \0 \0 001 n 302 @ 342 001 \0
c065 fe00 fb00 0001 0100 c26e e240 0001
0000140 377 231 244 206 323 276 350 251 L
99ff 86a4 bed3 a9e8 004c
0000151
rdb中对字符串数据的压缩:
可以在配置文件中修改下述字段去开启或者关闭压缩,默认是开启
# Compress string objects using LZF when dump .rdb databases?
# For default that's set to 'yes' as it's almost always a win.
# If you want to save some CPU in the saving child set it to 'no' but
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes
开启的情况下,大于20字节的字符串会进行压缩。小于20字节的字符串会以原样显示。
RDB文件的载入
RDB文件的生成有主动和被动方式两种,但是载入只有在服务器启动时,程序自动载入,没有手动载入的办法。
在载入的过程中,会阻塞服务器的请求,载入后才开始处理。
如果服务器已经开启了AOF持久化功能,那redis进程会优先加载aof的持久化文件,不会加载rdb的。 只有AOF功能关闭时,才会使用RDB文件加载。
注意点
- rdb持久化和aof持久化可以同时打开
- aof持久化打开时,如果对应的aof文件不存在,会创建一个新的,不会使用rdb的
所以,一旦rdb持久化和aof持久化同时打开, rdb中的数据就会完全被aof中的数据覆盖,如果一开始没有aof的文件,那rdb中的数据相当于会完全清空!!!
那如何将rdb的数据转到aof的文件中?
-
- 先切断网络
-
- 执行bgrewriteaof
-
- 恢复网络
-
- 再用aof持久化方式启动。
总结
- RDB 文件用于保存和还原 Redis 服务器所有数据库中的所有键值对数据。
- SAVE 命令由服务器进程直接执行保存操作,所以该命令会阻塞服务器。
- BGSAVE 令由子进程执行保存操作,所以该命令不会阻塞服务器。
- 服务器状态中会保存所有用 save 选项设置的保存条件,当任意一个保存条件被演足时,服务器会自动执行 BGSAVE 命令。
- RDB 文件是一个经过压缩的二进制文件,由多个部分组成。
- 对于不同类型的键值对,RDB 文件会使用不同的方式来保存它们。