一文让你理解Redis 的两种持久化方式:RDB和AOF

       众所周知,Redis是一种内存数据库,但是存在内存中的数据一断电或者服务节点宕机之后就消失了,在高并发场景下每秒千万级的访问量,那将会丢失大量的客户数据,这是不可容忍度的,但是我们的Redis肯定没有这么鸡肋!Redis也支持数据的持久化,Redis有两种持久化方式RDB(Redis DataBase)AOF(Append Only File)

一、RDB详解

       RDB持久化是将当前进程中的数据生成快照保存到硬盘,默认会在当前工作目录下生成dump.rdb文件,当Redis重启的时候会读取快照文件来恢复数据。RDB是Redis的默认持久化方式。有两种触发的方式:第一种是:手动在客户端使用save命令和bgsave命令触发;第二种是:在redis.conf配置文件中使用save <seconds> <changes>来配置后自动触发。

1、手动触发RDB

save命令和bgsave命令都可以生成RDB文件。

  • save命令会阻塞Redis服务器进程,直到RDB文件被创建出来为止,在Redis服务器阻塞期间,服务器无法处理任何请求。
  • bgsave命令会创建一个子进程(拷贝一份父进程),由子进程负责创建RDB文件,父进程(Redis主进程)可以继续处理请求。bgsave命令执行过程中,只有fork父进程时才会阻塞服务器进程,而对于save命令,整个执行过程都会阻塞服务器,因此在生产环境下要杜绝使用save的使用。而且在自动触发RDB持久化的时候,Redis也会选择bgsave而不是save
2、自动触发RDB

自动触发最常见的情况是在配置文件中通过save <seconds> <changes>命令来配置,即只要在自定的seconds秒内只要发生了changes次变化后,就会触发bgsave。redis默认的配置有三个:

save 900 1     //当时间到900秒时,在此期间如果redis数据发生了至少1次变化,则执行bgsave
save 300 10    //当时间到300秒时,在此期间如果redis数据发生了至少10次变化,则执行bgsave
save 60  10000  //当时间到900秒时,在此期间如果redis数据发生了至少1次变化,则执行bgsave
3、自动触发的原理

Redis的自动触发是通过serverCron函数、dirty计数器和lastave时间戳来实现的。

  • serverCron是Redis服务器的周期性操作函数,默认每隔100ms执行一次;该函数对服务器的状态进行维护,其中一项工作就是检查save <seconds> <changes> 配置的条件是否满足,如果满足就执行bgsave。

  • dirty计数器是Redis服务器维持的一个状态,记录了上一次执行bgsave/save命令后,服务器状态进行了多少次修改(包括增删改);而当save/bgsave执行完成后,会将dirty重新置为0。

  • lastsave时间戳也是Redis服务器维持的一个状态,记录的是上一次成功执行save/bgsave的时间。

也就是每隔100ms,执行serverCron函数;在serverCron函数中,遍历save <seconds> <changes> 配置的保存条件,只要有一个条件满足,就进行bgsave。对于每一个save <seconds> <changes> 条件,只有下面两条同时满足时才算满足:
    (1)当前时间-lastsave > seconds
    (2)dirty >= changes

4、其他自动触发机制

除了在配置文件中配置save <seconds> <changes> 来触发bgsave以外,还有别的情况会触发bgsave:

  • 在主从复制场景下,如果从结点执行全量复制操作,则主结点回执性bgsave命令,并将dump.rdb文件发送给从结点。
  • 在执行shutdown命令时,会自动执行rdb持久化,这一点通过redis的日志看到
5、RDB文件
5.1 RDB的文件格式

RDB文件是经过压缩的二进制文件,RDB的的文件格式如下图所示

其中各个字段的含义说明如下:

  • REDIS:常量,保存着”REDIS”5个字符。

  • db_version:RDB文件的版本号,注意不是Redis的版本号。

  • SELECTDB 0 pairs:表示一个完整的数据库(0号数据库),同理SELECTDB 3 pairs表示完整的3号数据库;只有当数据库中有键值对时,RDB文件中才会有该数据库的信息(上图所示的Redis中只有0号和3号数据库有键值对);如果Redis中所有的数据库都没有键值对,则这一部分直接省略。其中:SELECTDB是一个常量,代表后面跟着的是数据库号码;0和3是数据库号码;pairs则存储了具体的键值对信息,包括key、value值,及其数据类型、内部编码、过期时间、压缩信息等等。

  • EOF:常量,标志RDB文件正文内容结束。

  • check_sum:前面所有内容的校验和;Redis在载入RBD文件时,会计算前面的校验和并与check_sum值比较,判断文件是否损坏。

5.2 RDB的存储路径

RDB文件的存储路径既可以在redis.conf配置文件中配置,也可以在客户端通过命令动态设定:
* 在配置文件中可以设置dir来指定RDB文件的存放路径,redis默认是存放在当前工作目录下。也可以在配置文件中通过设置dbfilename指定RDB文件的名字,redis默认的文件名是dump
* 动态设定:Redis启动后也可以在客户端使用config set dir /path来动态的改变RDB的存放路径,当然也可以通过config set dbfilename newfilenaem来设置RDB文件的名字。

5.3 RDB文件的压缩

       Redis默认采用LZF算法对RDB文件进行压缩。虽然压缩会有一定的性能消耗,但是这样可以大大减小RDB文件的大小。但是需要特别注意的是:RDB文件的压缩并不是针对整个文件进行的,而是对数据库中的字符进行的,且只有在字符串达到一定长度(20字节)时才会进行。

6、启动时加载

       RDB文件的载入工作是在服务器启动的时候自动进行的,并没有专门的命令。但是当开启AOF后,Redis会优先加载AOF文件来恢复数据,只有当AOF关闭时,才会在Redis服务器启动的时候检测RDB文件,并自动加载。服务器载入RDB文件期间处于阻塞状态,直到加载完毕阻塞解除。Redis载入RDB文件时,会对RDB文件进行校验,如果文件损坏,则日志中会打印错误,Redis启动失败。

7、RDB优缺点总结

最后以一幅图的方式总结RDB的优缺点:

二、AOF(Append Only File)详解

       RDB持久化是将进程数据做成内存快照保存了起来,而AOF用了和RDB完全不同的做法,AOF将Redis每次写操作都记录到一个日志文件中,当Redis重启的时候会优先加载AOF(如果开启了AOF)文件,然后执行文件中的命令恢复数据,与RDB相比较AOF具有更好的实时性,也是当前主流的持久化方案。

1、开启AOF

Redis服务器默认开启的是RDB,没有与开启AOF,要开启AOF,需要在redis.conf中修改appendonlyyes,还可以在配置文件中修改AOF文件的名字等等,具体的可以参考我的这篇笔记:Redis配置文件redis.conf详解

2、AOF的执行流程

由于需要记录Redis的每条写命令,因此AOF不需要触发,AOF的执行流程包括:

  • 命令追加(append):将Redis的写操作追加到缓冲区aof_buf
  • 文件写入(write)和文件同步(sync):根据不同的同步策略将aof_buf中的内容同步带硬盘
  • 文件重写(rewrite):定期重写AOF文件,达到压缩的目的。
2.1 命令追加(append)

Redis先将写命令追加到缓冲区,而不是直接写入文件,主要是为了避免每次有写命令都直接写入硬盘,导致硬盘IO成为Redis负载的瓶颈。
命令追加的格式是Redis命令请求的协议格式,它是一种纯文本格式,具有兼容性好、可读性强、容易处理、操作简单避免二次开销等优点;具体格式略。在AOF文件中,除了用于指定数据库的select命令(如select 0 为选中0号数据库)是由Redis添加的,其他都是客户端发送来的写命令。

2.2 文件写入(write)和文件同步(sync)

Redis提供了多种AOF缓存区的同步文件策略,策略涉及到操作系统的write函数和fsync函数:

  • write函数:为了提高文件写入效率,在现代操作系统中,当用户调用write函数将数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区被填满或超过了指定时限后,才真正将缓冲区的数据写入到硬盘里。这样的操作虽然提高了效率,但也带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失
  • fsync函数:为了解决write函数数据丢失的问题,因此系统提供了fsync、fdatasync等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性。

AOF缓存的同步文件策略有appendfsync控制,Redis提供了三种策略:

  • always:命令写入aof_buf后立即调用系统的fsync函数同步到AOF文件,fsync完成后线程返回。这种情况下,每次有写命令都要同步到AOF文件,硬盘IO成为性能瓶颈,Redis只能支持大约几百TPS写入,严重降低了Redis的性能;即便是使用固态硬盘(SSD),每秒大约也只能处理几万个命令,而且会大大降低SSD的寿命。
  • no:命令写入aof_buf后调用系统的wirte函数,不对AOF文件做fsync同步,同步操作由系统负责,通常同步周期为30s。这种情况下,文件同步的时间不可控,且缓冲区中堆积的数据会很多,数据安全性无法保证。
  • everysec:命令写入aof_buf后调用系统的write函数,write完成后返回,fsync同步文件操作,有专门的线程每一秒调用一次。everysec是前面两种策略的折中,兼顾了性能和数据安全,也是Redis的默认配置。
2.3 文件重写(rewrite)

AOF文件重写主要的作用就是对AOF文件进行压缩,文件重写就会定期重写AOF文件,减小AOF文件的体积。需要注意的是,AOF重写只是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取。写入操作。

为什么文件重写可以压缩AOF文件?

  • 过期的数据不需要再写入文件
  • 无效的命令不再写入文件
  • 多条命令可以合并为一条命令,比如sadd stu v1, sadd stu v2 ,sadd stu v2,这三条操作可以合并为一条sadd stu v1 v2 v3。不过为了防止单条命令过大造成客户端缓冲区溢出,对于list、set、hash、zset类型的key,并不一定只使用一条命令;而是以某个常量为界将命令拆分为多条。这个常量在redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD中定义

总之压缩的原理就是通过重写减小命令的数量从而减少了文件的大小。

3、文件重写的触发

文件重写的触发有两种方式:第一种是在客户端使用bgrewriteaof命令触发;第二种是在配置文件中设置auto-aof-rewrite-min-sizeauto-aof-rewrite-percentage,以及aof_current_sizeaof_base_size状态确定触发时机。

  • 手动触发:直接调用bgrewriteaof命令,该命令的执行与bgsave有些类似—都是fork子进程进行具体的工作,且都只有在fork时阻塞。使用bgrewriteaof后,可以看到服务器的日志如下:
  • 自动触发:只是通过配置文件自动的触发重写,但是还是要使用bgrewirteaof这个命令,主要配置的参数如下:
    • auto-aof-rewrite-min-size:执行AOF重写时,文件体积最小体积,默认为64MB。
    • auto-aof-rewrite-percentage:执行AOF重写时,当前AOF大小和上一次重写AOF大小的比值,默认大小100。这些参数都可通过config get 参数来查看。
4、文件重写的流程

对照上图,可以总结出AOF文件的重写流程如下:

  1. Redis父进程首先判断当前是否存在正在执行 bgsave/bgrewriteaof的子进程,如果存在则bgrewriteaof命令直接返回,如果存在bgsave命令则等bgsave执行完成后再执行。

  2. 父进程执行fork操作创建子进程,这个过程中会阻塞Redis主进程。

  3. 父进程fork后,执行bgrewriteaof命令返回”Background append only file rewrite started”信息并不再阻塞父进程,此时Redis主进程恢复可以响应其他命令。Redis的所有写命令依然写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确。由于fork操作使用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然在响应命令,因此Redis使用AOF重写缓冲区(图中的aof_rewrite_buf)保存这部分数据,防止新AOF文件生成期间丢失这部分数据。也就是说,bgrewriteaof执行期间,Redis的写命令同时追加到aof_buf和aof_rewirte_buf两个缓冲区。

  4. 子进程根据内存快照,按照命令合并规则写入到新的AOF文件。

  5. 子进程重写完新的AOF文件后,向父进程发信号,父进程更新统计信息,具体可以通过info persistence查看。 接着父进程把AOF重写缓冲区的数据写入到新的AOF文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致。

5、AOF文件启动加载

当开启AOF后,Redis重启会默认优先加载AOF文件来恢复数据;只有当AOF关闭时参会加载RDB文件。Redis加载AOF文件时,会对AOF文件进行校验,如果文件损坏,则日志中就会打印错误,并且Redis会启动失败。当AOF文件损坏后,我们可以使用redis-check-aof 这个工具来修复AOF文件。

6、AOF优缺点总结

最后以一幅图片总结AOF的优缺点:

留言区

还能输入500个字符