Nivelle 开拓视野冲破艰险看见世界 身临其境贴近彼此感受生活

redis基础学习之RDB

2017-09-18

RDB

  • RDB持久化既可以手动执行,也可以根据服务器配置选项定期执行,该功能可以将某个时间点上的数据库状态保存到一个RDB文件中.

  • RDB持久化功能生成的RDB文件是一个经过压缩的二进制文件,通过该文件还可以还原成RDB文件时的数据库状态.

在 Redis 运行时, RDB 程序将当前内存中的数据库快照保存到磁盘文件中, 在 Redis 重启动时, RDB 程序可以通过载入 RDB 文件来还原数据库的状态。

RDB保存

  • SAVE:SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求:
redis > SAVE
ok

  • BGSAVE:命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求:

redis>BGSAVE
Background saving started

创建RDB文件的实际工作由rdb.c/rdbSave函数完成,SAVE命令和BGSAVE命令会以不同的方式调用这个函数.

def SAVE();


//创建RDB文件
rdbSave();


def BGSAVE(){
	pid =fork();

	if pid ==0;

	//子进程付出创建RDB文件

	rdbSave();

	//完成之后向父进程发送信号
	single_parent()

  elif pid > 0 :

  //父进程继续处理命令请求,并通过轮询等待子进程信号
  handle_request_and_wait_signal()
  else:
  //处理出错情况
  handle_fork_error();
}

和使用SAVE命令或者BGSAVE命令创建RDB文件不同,RDB文件的载入工作是在服务器启动时自动执行的,所以Redis并没有专门用于载入RDB文件的命令,只要Redis服务器在启动时检测到RDB文件的存在,它就会自动载入RDB文件.

因为AOF文件的更新频率比RDB文件更新频率高:

  • 如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件还原数据库状态

  • 只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据库状态.

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

SAVE

当 SAVE 执行时, Redis 服务器是阻塞的, 所以当 SAVE 正在执行时, 新的 SAVE 、 BGSAVE 或 BGREWRITEAOF 调用都不会产生任何作用。只有在上一个 SAVE 执行完毕、 Redis 重新开始接受请求之后, 新的 SAVE 、 BGSAVE 或 BGREWRITEAOF 命令才会被处理。

BGSAVE

在执行 SAVE 命令之前, 服务器会检查 BGSAVE 是否正在执行当中, 如果是的话, 服务器就不调用 rdbSave , 而是向客户端返回一个出错信息, 告知在 BGSAVE 执行期间, 不能执行 SAVE 。这样做可以避免 SAVE 和 BGSAVE 调用的两个 rdbSave 交叉执行, 造成竞争条件。

如果 BGSAVE 正在执行,那么 BGREWRITEAOF 的重写请求会被延迟到 BGSAVE 执行完毕之后进行,执行 BGREWRITEAOF 命令的客户端会收到请求被延迟的回复。 如果 BGREWRITEAOF 正在执行,那么调用 BGSAVE 的客户端将收到出错信息,表示这两个命令不能同时执行。

BGREWRITEAOF 和 BGSAVE 两个命令在操作方面并没有什么冲突的地方, 不能同时执行它们只是一个性能方面的考虑: 并发出两个子进程, 并且两个子进程都同时进行大量的磁盘写入操作, 这怎么想都不会是一个好主意。

自动间隔性保存

redis允许用户通过设置服务器的save选项,让服务器每隔一段时间就自动执行一次BGSAVE命令.用户可以通过save选项设置多个保存条件,但只要其中任意一个条件满足,服务器就会执行BGSAVE命令:

例如.如果我们提供以下选项:

save 900 1

save 300 10

save 60 10000

那么只要满足以下条件中的任意一个,BGSAVE命令就会被执行:

  • 服务器在900秒之内,对数据进行了至少一次修改

  • 服务器在300秒内,对数据库进行了至少10次修改

  • 服务器在60内,对数据库至少进行了10000次修改

设置保存条件

redis服务器启动时,用户可以通过制定配置文件或者传入启动参数的方式设置SAVE选项,如果用户没有主动设置SAVE选项,那么服务器会为SAVE选项设置默认条件:

save 900 1

save 300 10

save 60 10000

接着,服务器会根据save选项所设置的保存条件,设置服务器状态rediServer结构的saveparams属性:

struct redisServer{
	
	//记录了保存条件的数组
	struct saveparam * saveparams;
}


saveparams属性是一个数组,数组中的每个元素都是一个saveparam结构,每个saveparam结构都保存了一个save选项设置的保存条件:

struct saveparam{
	time_t seconds;

	int changes;
}


dirty计数器和lastsave属性

除了saveparams数组之外,服务器状态还维持着一个dirty计数器,以及一个lastsave属性:

  • dirty计数器记录上一次成功执行SAVE命令或者BGSAVE命令之后,服务器对数据库状态(服务器中的所有数据库)进行了多少次修改(包括写入,删除,更新等操作)

  • lastsave属性是一个UNIX时间戳,记录了服务器上一次成功执行SAVE命令或者BGSAVE命令的时间.

当服务器成功执行了一个数据库修改命令后,程序就会对dirty计数器进行更新:命令修改了多少次数据库,dirty计数器的值就增加多少.

检查保存条件是否满足

redis服务器周期性操作函数serverCron默认每隔100毫秒就会执行一次,该函数对正在运行的服务器进行维护,它的其中一项工作就是检查save选项所设置的保存条件是否已经满足,如果满足的话,就执行BGSAVE命令.

载入

在载入期间, 服务器每载入 1000 个键就处理一次所有已到达的请求, 不过只有 PUBLISH 、 SUBSCRIBE 、 PSUBSCRIBE 、 UNSUBSCRIBE 、 PUNSUBSCRIBE 五个命令的请求会被正确地处理, 其他命令一律返回错误。 等到载入完成之后, 服务器才会开始正常处理所有命令。

发布与订阅功能和其他数据库功能是完全隔离的,前者不写入也不读取数据库,所以在服务器载入期间,订阅与发布功能仍然可以正常使用,而不必担心对载入数据的完整性产生影响。

另外, 因为 AOF 文件的保存频率通常要高于 RDB 文件保存的频率, 所以一般来说, AOF 文件中的数据会比 RDB 文件中的数据要新。

因此, 如果服务器在启动时, 打开了 AOF 功能, 那么程序优先使用 AOF 文件来还原数据。 只有在 AOF 功能未打开的情况下, Redis 才会使用 RDB 文件来还原数据。

数据结构

RDB 文件的组织方式如下:

+-------+-------------+-----------+-----------------+-----+-----------+
| REDIS | RDB-VERSION | SELECT-DB | KEY-VALUE-PAIRS | EOF | CHECK-SUM |
+-------+-------------+-----------+-----------------+-----+-----------+

                      |<-------- DB-DATA ---------->|

键值对在 RDB 文件中的组织方式如下:

+----------------------+---------------+-----+-------+
| OPTIONAL-EXPIRE-TIME | TYPE-OF-VALUE | KEY | VALUE |
+----------------------+---------------+-----+-------+

RDB 文件使用不同的格式来保存不同类型的值。

总结

  • rdbSave 会将数据库数据保存到 RDB 文件,并在保存完成之前阻塞调用者。

  • SAVE 命令直接调用 rdbSave ,阻塞 Redis 主进程; BGSAVE 用子进程调用 rdbSave ,主进程仍可继续处理命令请求。

  • SAVE 执行期间, AOF 写入可以在后台线程进行, BGREWRITEAOF 可以在子进程进行,所以这三种操作可以同时进行。

  • 为了避免产生竞争条件, BGSAVE 执行时, SAVE 命令不能执行。

  • 为了避免性能问题, BGSAVE 和 BGREWRITEAOF 不能同时执行。

  • 调用 rdbLoad 函数载入 RDB 文件时,不能进行任何和数据库相关的操作,不过订阅与发布方面的命令可以正常执行,因为它们和数据库不相关联。


下一篇 I/O多路复用

评论