Redis持久化方式

[TOC]
redis持久化分为rdb和aof,rdb是将整个数据库保存为二进制文件,aof则是记录命令的方式保存。

一、rdb

rdb持久化功能所生成的rdb文件是一个经过压缩的二进制文件。
在载入rdb文件过程中,redis服务器一直处于阻塞状态,直到载入完毕。
在这里插入图片描述

创建rdb文件的两种方式

有两个命令可以用来保存rdb文件,save和bgsave。
不同的是,save命令会阻塞redis服务器进程,直到rdb文件创建完成,在此期间不再处理redis命令。
而bgsave则是fork父进程,由子进程负责rdb文件的创建,在子进程创建rdb文件过程中,redis服务器任然会处理redis命令。

1、save、bgsave、bgrewriteaof

1、为了避免产生竞争条件,在bgsave命令执行期间,redis服务器会拒绝save和bgsave命令
2、在bgsave命令执行过程中,客户端发送的bgrewriteaof命令会被延后到bgsave命令执行完
3、在bgrewriteaof命令执行过程中,客户端发送的bgsave命令会被拒绝

2、自动间隔性保存

用户可以通过设置服务器配置的save选项,让服务器每隔一段时间自动执行一次bgsave。

1
2
3
save 900 1       900秒内数据库至少修改1次
save 300 10 300秒内数据库至少修改10次
save 60 1000 60秒内数据库至少修改1000次

redis服务器使用saveparams数组来保存这些信息

1
2
3
4
struct saveparam {
time_t seconds;//秒数
int changes;//修改数
}
1
2
3
struct redisServer {
struct saveparam *saveprames;//记录保存条件的数组
}

在这里插入图片描述

3、dirty、lastsave属性

redis服务器维护了一个dirty计数器和lastsave属性。

  • dirty表示自上次执行save或者bgsave命令后服务器对数据库进行了多少次更新操作(包括增加,修改,删除)
  • lastsave是一个unix时间戳,记录服务器上一次执行save或者bgsave命令
    1
    2
    3
    4
    struct redisServer {
    long long dirty;//计数器
    time_t lastsave;//上一次执行保存的时间
    }
    redis服务器周期性操作函数serverCron默认每隔100毫秒就会执行一次,这个函数就会检查save选项配置是否已经满足,如果满足就执行bgsave命令。

    二、aof

    aof持久化是通过保存redis服务器执行的命令来实现持久化功能的。
    在这里插入图片描述

1、aof持久化实现

1、命令追加

redis服务器在执行完一个命令后,会以协议格式将被执行的命令追加到服务器状态的aof_buf缓冲区末尾。

1
2
3
struct redisServer {
sds aof_buf;//aof缓冲区
}

2、aof文件的写入和同步

redis服务器实际上就是一个事件循环,这个循环中的文件事件会处理客户端的命令请求;时间事件则处理如serverCron函数这样需要定时运行的函数。
服务器在每次结束一个事件循环之前,都会调用flushAppendOnlyFile函数,考虑是否将aof_buf缓冲区中的内容写入和保存到aof文件里面。
在这里插入图片描述

1、always

服务器在每个事件循环都要将aof_buf中的内容写入并且同步到aof文件中,always是三个选项中效率最慢的,但即使出现故障停机,aof持久化也只会丢失一个事件循环中的命令数据

2、everysec

服务器在每个数据循环都要将aof_buf中的内容写入aof文件,并且每个一秒就要在子线程中对aof文件进行同步。everysec效率足够快,如果出现故障停机只会丢失一秒钟信息。

3、no

服务器在每个事件循环中都要对aof_buf中的内容写入到aof文件中,至于何时对aof文件进行同步则要看操作系统的调度。no是三者中效率最高的,但单次同步时间最长。

2、aof文件载入与数据还原

redis服务器读取aof文件的顺序步骤

  • 创建一个不带网络连接的伪客户端(因为redis命令只能在客户端上下文中执行)
  • 从aof文件中读取一条写命令
  • 使用伪客户端执行读取的命令
  • 循环,直到所有的命令都被处理完毕

3、aof重写

对于aof持久化来说,如果事无巨细的将全部命令保存下来,随着redis不间断运行,那势必会造成aof文件无比巨大,使得载入数据的时候会多很多无用功。
为了解决aof文件体积过大的问题,redis提供了aof文件重写的功能。通过这个功能,redis会创建一个新的aof文件来代替旧的aof文件。新旧aof文件数据库状态相同,但新aof文件不会包含冗余的命令。

1、重写实现

新的aof文件不会对旧的aof文件造成任何影响,新的aof文件由当前数据库状态得来。
重写前

1
2
3
4
sadd a 1
sadd a 2
sadd a 3
srem a 1

重写后

1
sadd a  2 3

值得注意的一点是,为了避免执行命令过程中造成客户端输入缓冲区溢出,重写程序在处理列表、哈希表、集合、有序集合这些可能带有多个元素的时候,会先检查元素的数量超过了redis.h/REDIS_AOF_REWRITE_LITEMS_PER_CMD(64)常量时,会使用多条命令来代替单条命令。

2、后台重写

由于aof重写程序aof_rewrite会进行大量的文件写入操作,所以调用这个函数的线程会被长时间的阻塞,redis服务器使用单线程处理命令请求,如果由服务器直接调用aof_write函数,那么在aof文件重写期间,服务器将无法处理命令请求。
redis将aof重写程序放到子进程中执行,子进程获得了父进程的数据副本,避免了锁的使用。同时父进程也可以继续处理命令请求。

由于在子进程中执行aof重写程序,子进程获取到的只是父进程的数据副本,在aof重写过程中,父进程可能会处理其它的命令请求,这就会使得数据库状态与重写后的aof文件不一致。
为了解决数据不一致的问题,redis服务器设置了一个aof重写缓冲区,这个缓冲区在服务器创建子进程之后才开始使用。redis服务器在执行完一个写命令后,会将这个写命令同时发送给aof缓冲区和aof重写缓冲区。

当子进程完成aof重写工作,子进程会向父进程发送一个信号,父进程接受到该信号后,会调用一个信号处理函数,这个函数会执行一下操作。

  • 将aof重写缓冲区内容写入到新的aof文件,这是新的aof文件所保存的数据库状态与redis服务器当前数据库状态保持一致
  • 对新的aof文件进行改名,原子覆盖现有的aof文件,完成两个文件的新旧交替。
    这个函数执行完毕后,父进程就会像往常一样接受命令请求。

在整个aof后台重写过程中,只有信号处理函数执行过程中才会对父进程造成阻塞,在其它时候,aof后台重写都不会阻塞父进程。
aof后台重写也就是bgsavewriteaof命令。

三、aof与rdb的不同

1、保存格式不同

rdb文件是一个经过压缩的二进制文件,aof文件则是记录命令。

2、使用优先级

aof更新频率比rdb高,当redis服务器开启了aof持久化功能,那么服务器会优先使用aof文件来还原数据库,只有在aof持久化功能关闭时,才使用rdb文件。
在这里插入图片描述