参考:
Redis是一个内存数据库,它将所有的数据都存储在内存中,所以一旦服务器进程退出,那么这些数据都将丢失。因此,需要将数据持久化到文件中,便于下次启动Redis服务时进行恢复。Redis提供了两种方式持久化:
RDB是默认的持久化方式。我们可以通过命令形式手动执行持久化,也可以通过配置文件方式配置触发条件自动执行持久化。默认情况下,redis.conf配置文件中设置了RDB持久化方式的触发条件以及其他相关配置。
RDB持久化生成的RDB文件是一个经过压缩的二进制文件,通过该文件可以还原生成RDB文件时的数据库状态。
这里主要用到两个命令: SAVE
和 BGSAVE
。
注:
SAVE
或 BGSAVE
命令后,可以看到在redis目录下生成了dump.rdb
文件,在服务器重启后将会使用这个文件进行恢复。对于这个文件的配置方式在1.2节有说明。BGSAVE
命令执行期间,客户端若发送SAVE
、BGSAVE
命令会被服务器拒绝,防止产生竞争条件。Redis允许通过配置 save
选项,让服务器在满足一定条件下自动执行一次BGSAVE
命令。
(1)具体配置在redis.conf文件中有详细说明,默认的RDB配置如下:
# save
# 配置触发条件
save 900 1
save 300 10
save 60 10000
# yes,当bgsave失败时,Redis服务器将停止接受写入操作
stop-writes-on-bgsave-error yes
# yes,启动压缩
rdbcompression yes
# yes,在文件末尾创建一个CRC64校验和
rdbchecksum yes
# 文件名称
dbfilename dump.rdb
# 文件写入目录
dir ./
参数说明:
注:修改这些配置后需重启redis服务使其生效。
RDB文件的载入是在服务器启动时自动执行的,Redis中没有专门用于载入RDB文件的命令。只要Redis服务器启动时检测到RDB文件存在,它就会自动载入RDB文件。
Redis服务器在载入RDB文件期间会一直处于阻塞状态,直到载入工作完成。
(1)示例:
连接redis客户端,执行SAVE
或 BGSAVE
命令后,放入数据,此时关闭redis服务器,再重新启动,可以看到会有从磁盘加载数据的日志。启动后连接服务器查看数据是否存在。
redis> save
OK
redis> bgsave
Background saving started
redis> set num 1
OK
redis> shutdown
not connected>
# 重启
$ ./redis-server ./redis.conf
# 日志
5434:M 11 Jul 2019 18:49:32.933 # Server initialized
5434:M 11 Jul 2019 18:49:32.934 * DB loaded from disk: 0.001 seconds
5434:M 11 Jul 2019 18:49:32.934 * Ready to accept connections
# ...
redis> get num
"1"
(1)设置保存条件
默认情况下,save命令配置如下:
save 900 1
save 300 10
save 60 10000
这些保存条件由设置服务器状态的redisServer结构的saveparams属性保存:
struct redisServer {
// ...
// 记录了保存条件的数组
struct saveparam *saveparams;
// ...
}
saveparams属性是一个数组,数组中每个元素是一个saveparam结构,每个saveparam结构都保存了一个save选项设置的保存条件:
struct saveparam {
// 秒数
time_t seconds;
// 修改数
int changes;
}
默认情况save条件下服务器状态的saveparams属性如下图所示:
(2)dirty计数器(属性)和lastsave属性
除了saveparams属性外,服务器状态还维护还维持着一个dirty计数器、lastsave属性:
SAVE
命令或BGSAVE
命令之后,服务器对数据库状态(服务器中的所有数据库)进行了多少次修改(写入、删除、更新等操作)SAVE
命令或BGSAVE
命令的时间服务器状态的redisServer结构的dirty、lastsave属性如下:
struct redisServer {
// ...
// 记录了保存条件的数组
struct saveparam *saveparams;
// 修改计数器
long long dirty;
// 上一次执行保存的时间
time_t lastsave;
// ...
}
当服务器成功执行一个数据库修改命令之后,程序就会对dirty计数器(属性)进行更新:命令修改了多少次数据库,dirty计数器的值就会增加多少。
(3)检查条件是否满足
Redis服务器的周期性操作函数serverCron默认每隔100ms就会执行一次,这个函数用于对正在运行的服务器进行维护,其中一项工作就是检查save选项所设置的保存条件是否满足,满足的话就执行BGSAVE
命令。
serverCron函数检查保存条件过程如下:
AOF持久化方式默认不开启;它是保存对Redis数据库的写操作(如set、sadd、rpush等命令)来记录数据库状态,并追加到AOF文件中;Redis 重启时,通过载入和执行AOF文件中保存的命令来还原服务器的数据库状态。
AOF持久化方式的数据一致性会比RDB持久化方式要高。
AOF相关配置参数如下:
# 持久化文件保存目录
dir ./
# 开启AOF持久化,默认no关闭,yes开启
appendonly no
# AOF持久化文件名
appendfilename "appendonly.aof"
# AOF持久化触发条件
appendfsync no
# AOF重写触发条件
auto-aof-rewrite-percentage 100
# AOF重写触发条件
auto-aof-rewrite-min-size 64mb
参数说明:
(1)AOF持久化触发条件(控制何时将aof缓冲区内容写入AOF文件)
appendfsync三个可选值:
注:Redis服务器接收到写入命令时,Redis会将该命令追加到AOF文件中。实际上,操作系统维护了一个缓冲区,Redis的命令首先会被写入到这个缓冲区中,而缓冲区中的数据必须被刷新到磁盘中才能被永久保存。这个过程是通过Linux系统调用 fsync() 完成的。这是一个阻塞调用,只有磁盘报告设备缓冲区中的数据写入完成之后才会返回。
appendfsync
参数可选值也就是控制fsync()调用的频率。(1)AOF文件
被写入AOF文件的所有命令都是以Redis的命令请求协议格式(纯文本格式)保存的。
redis> set num 1
OK
redis> get num
"1"
redis> set num 2
OK
select 0
:选择0号数据库(16个数据库,0-15);之后是两个修改命令set num 1
set num 2
*2\r\n$6\r\nSELECT\r\n$1\r\n0\r\n
*3\r\n$3\r\nset\r\n$3\r\nnum\r\n$1\r\n1\r\n
*3\r\n$3\r\nset\r\n$3\r\nnum\r\n$1\r\n2\r\n
(2)AOF文件的载入与数据还原
AOF文件里包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一遍AOF文件里保存的写命令,就可以还原服务器关闭之前的数据库状态。
Redis读取AOF文件并还原数据库状态的步骤如下:
如果操作系统崩溃,那么AOF文件最后可能会损坏或截断。Redis提供了一个工具,redis-check-aof,用于修复损坏的文件。例如修复一个损坏的AOF文件,可以执行以下命令:
$ bin/redis-check-aof --fix appendonly.aof
Redis将命令不断追加到AOF文件中,这个文件的大小会显著增加,这就会使得重启时恢复数据库状态比较慢。Redis提供了一种机制,即AOF重写来压缩AOF文件。
通过该功能,Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件,新旧两个AOF文件所保存的数据库状态相同,但新的AOF文件不会包含任何浪费空间的冗余命令,所以重写后的AOF文件通常比旧文件要小得多。
例如以下几种可能的情况:
AOF就是利用这些情况,删除多余的键或只存储最新的键等操作对文件进行数据压缩。
(1)两种重写方式
BGREWRITEAOF
命令来启动重写过程。(2)重写实现
Redis中重写程序是在子进程中实现,这样有2个目的:
AOF文件重写的问题:
问题:子进程在进行AOF重写期间,服务器进程还需要继续处理命令请求,而新的命令可能会对现有的数据库状态进行修改,使得服务器当前的数据库状态和重写后的AOF文件所保存的数据库状态不一致。
解决方法:为了解决重写期间数据不一致情况,Redis服务器设置了一个AOF重写缓冲区,这个缓冲区在服务器创建子进程时开始使用,当Redis服务器执行完一个写命令后,它会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区。流程如下图所示:
当子进程完成AOF重写工作后,它会向父进程发送一个信号,父进程在接收到该信号后,调用一个信号处理函数,执行以下工作:
上述调用信号处理函数工程是阻塞的,会对服务器进程(父进程)造成阻塞,在重写期间的其他时候不会阻塞,结束后父进程正常接受客户端请求。
注:以上就是AOF重写的实现,也是BGREWRITEAOF
命令的实现原理。
(3)重写后的AOF文件
对2.2节生成的AOF文件再手动执行下AOF重写:
redis> BGREWRITEAOF
Background append only file rewriting started
打开appendonly.aof文件,变成了以下内容:
REDIS0009ú redis-ver^E5.0.3ú
redis-bitsÀ@ú^EctimeÂÑÍ/]ú^Hused-memÂ0Ø^O^@ú^Laof-preambleÀ^Aþ^@û^A^@^@^CnumÀ^Bÿ=òêú<88>)"G
RDB优点:
RDB缺点:
AOF优点:
redis-check-aof
工具修复。AOF缺点:
注:
如果同时使用了RDB和AOF持久化方式,在启动服务器时: