redis持久化——RDB、AOF

redis支持两种持久化方案:RDB和AOF。

RDB

RDB持久化是redis默认的,用来生成某一个时间点的数据快照;RDB是一个经过压缩的二进制文件,采用RDB持久化时服务器只会保存一个RDB文件(维护比较简单);

  • 每次进行RDB持久化时,redis都是将内存中完成的数据写到文件中,不是增量的持久化(写脏数据)
  • 写RDB文件时,先把内存中数据写到临时文件,然后替换原来的RDB文件;

1、RDB文件的生成:
RDB持久化可以手动执行,也可以按照服务器的配置定期执行。
1)save和bgsave命令:(手动用于生成RDB文件的命令)

  • save:会阻塞redis服务器进程,直到创建RDB文件完毕为止;(在此期间进程不能处理任何请求)
  • bgsave:fork一个子进程来创建RDB文件,父进程可以继续处理命令请求;

2)自动执行:
redis服务器允许用户通过设置配置文件save选项,让服务器每隔一段时间自动执行一次bgsave命令。如下:配置文件中的save选项
save 900 1
save 300 10
save 60 10000

服务器内部维护了一个dirty计数器和lastsave属性:

  • dirty:记录了距上次成功执行了save或bgsave命令之后,数据库修改的次数(写入、删除、更新等);
  • lastsave:unix时间戳,记录了上一次成功执行save或bgsave命令的时间;

redis服务器的周期性操作函数serverCron默认每100毫秒执行一次,该函数的一个主要作用就是检查save选项所设置的保存条件是否满足,如果满足就执行bgsave命令。检查的过程:根据系统当前时间、dirty和lastsave属性的值来检验保存条件是否满足。

补充:

  • 在执行bgsave期间,客户端发送的save、bgsave命令会被服务器拒绝执行(防止产生竞争);
  • 如果bgsave在执行,bgrewriteaof命令会被延迟到bgsave执行完毕后再执行;
  • 如果bgrewriteaof在执行,bgsave命令也会被延迟到bgrewriteaof命令执行完毕后再执行(bgsave和bgrewriteaof都是通过子进程完成的,不存在冲突,主要是考虑性能);

2、RDB文件的载入:

  • redis并没有专门的命令去载入RDB文件,只有在服务器启动的时候检测到RDB文件存在就会自动执行载入。
  • 如果redis启用了AOF持久化功能,那么服务器优先使用AOF文件还原数据。
  • 当服务器载入RDB文件时,会一直处于阻塞状态,直到载入完毕为止。
  • 载入时RDB文件时,系统会自动检查、如果是过期键不会加载到数据库中。

3、其他:
1)redis会根据配置文件中rdbcompression属性决定保存RDB文件时是否进行压缩存储(默认为yes),如果打开了压缩功能,redis会判断字符串长度>=20字节则压缩,否则不压缩存储;
2)redis自带了RDB文件检查工具redis-check-dump;
3)使用od命令打印RDB文件:[root@centOS1 dir]# od -oc dump.rdb
4)RDB文件名通过配置文件dbfilename dump.rdb指定,存放位置通过配置文件dir /var/lib/redis/ 指定;

AOF

有上面分析可知:RDB方式持久化的颗粒比较大,当服务器宕机时,到上次save或bgsave后的所有数据都会丢失。而AOF的持久化颗粒比较细,当服务器宕机后,只有宕机之前没来得AOF的操作数据会丢失。

1、AOF实现:

1)AOF持久化是通过保存redis服务器所执行的写命令来记录数据库状态的;被写入AOF文件的所有命令都是以Redis的命令请求协议格式保存的(Redis的请求协议是纯文本的)。服务器在启动时,通过载入AOF文件、并执行其中的命令来还原服务器状态。

2)AOF文件名通过配置文件appendfilename appendonly.aof 指定,存放位置通过配置文件dir /var/lib/redis/ 指定;

3)AOF步骤:

  • 命令追加:服务器在执行玩一个写命令后,会以协议的格式把其追加到aof_buf缓冲区末尾;
  • 文件写入:redis服务器进程就是一个事件循环,在每次事件循环结束,会根据配置文件中的appednfsync属性值决定是否将aof_buf中的数据写入到AOF文件中;
  • 文件同步:将内存缓冲区的数据写到磁盘;(由于OS的特性导致)

补充:

为了提高文件写入效率,现代OS中通常会把写入数据暂时保存在一个内存的缓冲区里面,等到缓冲区满或超时,一次性把其中的内容再写到磁盘。

4)appendfsync选项:

  • always:将aof_buf中所有内容写入、同步到AOF文件;
  • everysec:将aof_buf中所有内容写到AOF文件,如果上次同步AOF文件时间距当前时间超过1s,那么对AOF文件同步;(由专门线程负责)
  • no:将aof_buf中所有内容写入AOF文件,合适同步根据操作系统决定;

5)AOF持久化效率和安全性:(appendfsync选项控制)
always每次都要写入、同步,所以其安全性最高,效率是最慢的;everysec效率也足够快,也安全性也可以得到保证;no效率最高,但安全性比较差。

2、AOF文件载入:
AOF文件中记录了数据库中所有写操作的命令,所以服务器只需要重新执行一遍AOF文件中的命令即可恢复服务器关闭之前的状态。步骤如下:

  • 创建一个不带网络连接的伪客户端;
  • 从AOF文件中分析并读取一条写命令;
  • 使用伪客户端执行被读出的写命令;

3、AOF重写:
由于AOF记录了数据库的所有写命令,所以随着服务器的运行,AOF文件中内容会越来越大。实际上,对于一个键值,由于多次的修改,会产生很多写命令;中间的一些写操作可以直接省去,直接把最终的键值信息记录到AOF文件中即可,从而减小AOF文件大小。

1)AOF重写:
为了解决AOF文件体积膨胀问题,redis服务器使用了AOF重写功能:创建一个新的AOF文件来代替现有的AOF文件,新旧两个AOF文件所保存的数据库状态相同,但新AOF文件不会包含任何浪费空间的冗余命令,所以新AOF文件体积会比旧AOF文件体积小很多。

2)原理:
AOF文件重写并不需要对现有AOF文件进行任何读取、分析或者写入操作,这个功能是通过读取服务器当前的数据库状态来实现的。首先,从数据库中读取键现在的值,然后使用一条命令去记录键值对,代替之前记录这个键值对的多条命令,这就是AOF重写的原理。

注:

为了避免在执行命令时造成客户端缓冲区溢出,重写程序在处理集合、链表等发现带有多个元素时,会检查元素数量是否超过redis默认的64,如果超过了,redis会使用多个命令代替这一条命令。

3)bgrewriteaof命令、AOF重写缓冲区:
由于redis是单进程的,为了不在进行重写时阻塞服务,redis使用了子进程的方式进行AOF重写。——bgrewriteaof

在使用子进程进程AOF重写时会产生另一个问题:子进程在AOF重写时,服务器主进程还在继续处理命令请求,而新的命令可能会对现有数据库状态进行修改,而从使得服务器当前状态和重写后的AOF文件所保存的服务器状态不一致。为了解决这个问题,redis引入了AOF重写缓冲区。

AOF重写缓冲区是在服务器创建子进程之后开始使用:
  • 执行客户端命令;
  • 将执行后的写命令追加到aof_buf(AOF缓冲区);
  • 将执行后的写命令追加到AOF重写缓冲区;

aof_buf(AOF缓冲区)会定期被写入、同步到AOF文件;而在AOF重写期间新的命令会写到AOF重写缓冲区。当AOF重写完成后,会向父进程发送一个信号,父进程收到信号后会阻塞当前服务,进行如下操作:

  • 将AOF重写缓冲区中的写命令写入到新的AOF文件;(保证了新AOF文件数据库状态和当前数据库状态的一致)
  • 对新的AOF文件重命名、原子的覆盖旧的AOF;

注:

整个AOF重写中,只有信号处理是阻塞的;当信号处理完毕后父进程就可以接收命令请求了。

4、如果redis开始基于rdb进行的持久化,之后通过appendonly yes 打开了aof,这时重新启动redis后会根据aof进行载入,所以原来所有的数据无法加载到数据库中。