> 文档中心 > RDB和AOF持久化底层原理详解

RDB和AOF持久化底层原理详解

RDB和AOF持久化底层原理详解

  • 1. 导言
  • 2. RDB持久化
    • 2.1 RDB文件的创建和载入
    • 2.2 save命令和bgsave命令执行时服务器状态
    • 2.3 自动间隔保存
    • 2.4 RDB文件结构
  • 3. AOF持久化
    • 3.1 AOF持久化的实现
      • 3.1.1 命令追加
      • 3.1.2AOF文件写入与同步
    • 3.2 AOF载入与数据还原
    • 3.3 AOF重写
    • 3.31 AOF重写的具体实现
    • 3.3.2 AOF后台重写

1. 导言

由于我们的 Redis是一款内存性数据库,所有数据都保存在内存中。一旦断电,那么数据将会丢失,所以为了解决这个问题,Redis使用两种持久化方式进行保存,将这些内存中的数据保存到磁盘上。

2. RDB持久化

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

2.1 RDB文件的创建和载入

有两个命令可以手动生成RDB文件,一个是save命令,会阻塞服务器进程,直到持久化完成;一个是bgsave命令,会在后台创建一个子进程,由子进程进行持久化操作,服务器进程可以继续工作
两个命令的区别如下:

def SAVE(): # 创建RDB 文件 rdbSave() def BGSAVE(): # 创建子进程 pid = fork() if pid == 0: # 子进程负责创建RDB 文件 rdbSave() #完成之后向父进程发送信号 signal_parent() elif pid > 0: # 父进程继续处理命令请求,并通过轮询等待子进程的信号 handle_request_and_wait_signal() else:# 处理出错情况handle_fork_error()

服务器文件的载入是在服务器启动时自动执行的,在启动时会自动检测是否有dump.rdb文件,有则自动载入

这里需要注意的是:因为我们后面讲的AOF通常会比我们的RDB文件更新频率高,所以:

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

判断流程如下:
RDB和AOF持久化底层原理详解

2.2 save命令和bgsave命令执行时服务器状态

save命令执行时由于是阻塞主线程的,所以并不能执行其他命令;

bgsave命令执行时,服务器线程是可以继续接受请求的。但是在执行save,bgsave,bgrewriteaof命令是不一样的。

  • 在bgsave执行期间,再次执行save,bgsave命令会被拒绝,因为它们保存时都会执行rdbSave函数的调用,为了避免产生冲突。
  • 在bgsave执行期间,bgrewriteaof会被延迟到bgsave执行完后才执行
  • 在bgrewriteaof执行期间,bgsave会被拒绝。
    bgrewriteaof和bgsave两个命令都是由子进程完成的,它们之间并没有冲突,只是为了性能考虑,同时执行大量的磁盘写入,这对性能是很大的影响

2.3 自动间隔保存

因为bgsave不会阻塞服务器进程,所以redis可以通过配置文件来让redis每隔一段时间自动执行一次bgsave命令
如下所示(这个也是Redis的默认设置):

save 900 1 # 服务器在900s之内,至少执行了1次修改操作,那么就会触发自动执行save 300 10 # 服务器在300s之内,至少执行了10次修改操作,那么就会触发执行save 60 10000 # 服务器在60s之内,至少执行了10000次修改操作,那么就会触发执行

RDB和AOF持久化底层原理详解

图2.1 redisServer结构中的saveparam属性

RDB和AOF持久化底层原理详解

图2.2 saveparam数组结构 如图2.1,2.2所示,服务器会根据save选项来设置redisServer结构中的saveparam属性,saveparam数组结构中的seconds表示时间,changes表示修改次数,默认结构如图2.3所示。

RDB和AOF持久化底层原理详解

图2.3 服务器状态中的保存条件

RDB和AOF持久化底层原理详解

图2.4 redisServer结构中的dirty属性和lastsave属性 如图2.4所示,redisServer中还有两个属性:dirty属性和lastsave属性。

  • dirty属性表示执行完上一次持久化操作后,数据库被修改了多少次
  • lastsave属性表示上一次执行持久化操作的时间。

那么redis服务器又是如何检查是否该自动执行持久化操作的呢??
Redis服务器的周期性函数serverCron默认每隔100ms执行一次,其中就包括检查save里的保存条件是否满足,满足则则执行bgsave命令
具体伪代码如下:

def serverCron(): # ... # 遍历saveparam中所有保存条件 for saveparam in server.saveparams: # 计算距离上次执行保存操作有多少秒 save_interval = unixtime_now()-server.lastsave # 如果数据库状态的修改次数超过条件所设置的次数 # 并且距离上次保存的时间超过条件所设置的时间 # 那么执行保存操作 if server.dirty >= saveparam.changes and save_interval > saveparam.seconds: BGSAVE() # ...

2.4 RDB文件结构

RDB和AOF持久化底层原理详解

图2.5 RDB文件结构 第一个字段REDIS:表示常量,可以帮助redis快速检查是否rdb文件

第二个字段db_version:表示RDB文件版本
第三个字段databases:表示多个数据中的键值对数据
第四个字段EOF:表示了数据库所有键值都已经写入完毕,结束符
第五个字段check_sum:表示校验和,可以检查前面的数据是否受到破坏

3. AOF持久化

AOF也是用来持久化的,只不过与RDB不同的点在于:RDB是通过保存数据库的键值对来进行持久化的,而AOF则是通过记录redis所执行的写命令来进行持久化的。

3.1 AOF持久化的实现

AOF持久化功能可以分为命令追加,文件写入,文件同步三个步骤

3.1.1 命令追加

RDB和AOF持久化底层原理详解

图3.1 redisServer结构中的aof_buf属性 如图3.1,redis服务端中有一个aof_buf缓冲区,服务器在执行完一个命令后,这个命令会被追加到aof_buf缓冲区内。

3.1.2AOF文件写入与同步

服务器进程就是一个事件循环,每个循环负责接受客户端请求和响应客户端。而时间时间则负责执行像serverCron函数这样需要定时执行的函数

服务端在每次结束一次时间循环时,就会调用一次flushAppendOnlyFile函数,考虑将aof_buf中的数据写到aof文件中

说的是考虑,自然就有不同的选项供你选择,总不可能是redis自己想的吧!!!

flushAppendOnlyFile执行的行为由服务器配置的appendfsync来决定,如图3.2所示。
RDB和AOF持久化底层原理详解

图3.2 redis配置文件中的appendfsync选项

appendfsync选项的值 flushappendOnlyFile的行为
always 将aof_buf里的数据写入并同步到AOF文件
everysec (Redis的默认选项) 将aof_buf里的数据写入到AOF文件,如果距离上次同步的时间超过1s,则再次同步AOF文件 ,并且这个是由新创建的线程执行。我在这里卡了一下,Redis不是单线程的吗??这里的单线程只是说redis单线程执行读写命令而已,其他的持久化存储模块,集群模块等其实是多进程多线程的
no 将aof_buf里的数据写入AOF文件,什么时候同步由操作系统决定

这里插播一条概念:现在的操作系统中,当用户调用write函数将数据写入到文件时,操作系统会先将其写入到一个文件缓冲区中,等到文件缓冲区满了或者超过时间,才会将其真正写入磁盘。(上述的同步就是将文件缓冲区里的数据写到磁盘)
这种做法虽然可以提高效率,但是如果电脑突然停机了,那么就会丢失一部分数据,为此系统提供了fsync和fdatasync函数,用于强制将文件缓冲区里的数据写入磁盘(上述的同步就是调用了这个函数,强制同步到磁盘)

三种选项的对比:
always:是每次都强制同步到磁盘中,所以效率最慢,电脑故障丢失的是一个时间循环里的数据
everysec:每次写入到缓冲区中,如果超过1s,则强制同步到磁盘,电脑故障丢失1s内的数据
no:每次写入到缓冲区内,写入磁盘由操作系统决定;写的比较快,但同步时间较长。电脑故障丢失前一次同步后的所有数据

3.2 AOF载入与数据还原

要还原AOF文件,那么只需要将命令重新执行一遍即可。

RDB和AOF持久化底层原理详解

图3.3 AOF数据恢复

3.3 AOF重写

如上面所讲的,每个命令都写入到AOF文件中,那么后面文件将会变得很大,且有很多不必要的冗余。比如说前面添加了一个元素,后面又删除了这个元素,数据库里没有什么变化,但是aof文件里多了两条命令。

为了解决这个问题,redis提供了AOF文件重写,当AOF超过一定大小时,就会开启重写。

3.31 AOF重写的具体实现

虽然叫AOF重写,但是与原来的AOF文件并没有关系,并不需要分析原来的AOF文件,只需要对当前数据的状态进行分析即可。最后完成后才替换掉旧的AOF文件。

RDB和AOF持久化底层原理详解

图3.4 AOF重写流程

因为重写的AOF只包含还原数据库必要的命令,所以可以压缩很多空间。

3.3.2 AOF后台重写

Redis将AOF重写程序放到子进程里执行,好处:

  • 子进程进行AOF重写期间,服务器进程可以继续处理命令请求
  • 子进程带有数据库副本,可以在避免锁情况下保证数据安全。

但是使用子进程也有一个问题:就是子进程带的是数据副本,而在AOF重写期间,父进程继续执行写命令,那么就会造成数据不一致问题

为了解决这个问题,Redis服务器设置了一个AOF重写缓冲区,这个缓冲区在重写开始时进行使用。
RDB和AOF持久化底层原理详解

图3.5 服务器同时将命令发给AOF缓冲区和AOF重写缓冲区

所以在子进程执行重写期间,服务端要执行三个工作:

  1. 执行客户端发来的命令
  2. 将命令追加到AOF缓存区,这样的话对现有的AOF文件的工作会如常进行。并且如果电脑故障的话,命令丢失也只与appendfsync选项有关
  3. 将命令追加到AOF重写缓冲区,这样的话可以用来后面保证新旧AOF文件数据一致性

当子进程完成AOF重写工作后,它会向父进程发送一个信号,父进程接收到后会调用一个信号处理函数:

  • 将AOF重写缓冲区里的数据写入子进程创建的新AOF中
  • 将新AOF替换旧的AOF

在整个重写过程中,只有父进程需要调用信号处理函数,才会阻塞线程,对redis性能影响降到了最低

这也是bgrewriteaof的实现原理

开发者涨薪指南 RDB和AOF持久化底层原理详解 48位大咖的思考法则、工作方式、逻辑体系