前言
Redis 是内存数据库,数据都是存储在内存中,为了防止过程退出导致数据的永恒失落,须要定期将 Redis 中的数据以数据或命令的模式从内存保留到本地磁盘。当下次 Redis 重启时,利用长久化文件进行数据恢复。Redis 提供了 RDB 和 AOF 两种长久化机制,前者将以后的数据保留到磁盘,后者则是将每次执行的写命令保留到磁盘(相似于 MySQL 的 Binlog)。本文将具体介绍 RDB 和 AOF 两种长久化计划,包含操作方法和长久化的实现原理。
注释
Redis 是一个基于键值对(K-V)存储的数据库服务器,上面先介绍 Redis 数据库的外部结构以及 K-V 的存储模式,有助于咱们更容易了解 Redis 的长久化机制。
- Redis 数据库构造
一个单机的 Redis 服务器默认状况下有 16 个数据库(0-15 号),数据库的个数是可配置的。Redis 默认应用的是 0 号数据库,能够应用 SELECT 命令切换数据库。
Redis 中的每个数据库都由一个 redis.h/redisDb 构造示意,它记录了单个 Redis 数据库的键空间、所有键的过期工夫、处于阻塞状态和就绪状态的键、数据库编号等等。
typedef struct redisDb {
// 数据库键空间,保留着数据库中的所有键值对
dict *dict;
// 键的过期工夫,字典的键为键,字典的值为过期事件 UNIX 工夫戳
dict *expires;
// 正处于阻塞状态的键
dict *blocking_keys;
// 能够解除阻塞的键
dict *ready_keys;
// 正在被 WATCH 命令监督的键
dict *watched_keys;
struct evictionPoolEntry *eviction_pool;
// 数据库编号
int id;
// 数据库的键的均匀 TTL,统计信息
long long avg_ttl;
} redisDb;
复制代码因为 Redis 是一个键值对数据库(key-value pairs database),所以它的数据库自身也是一个字典,对应的构造正是 redisDb。其中,dict 指向的是一个记录键值对数据的字典,它的键是一个字符串对象,它的值则能够是字符串、列表、哈希表、汇合和有序汇合在内的任意一种 Redis 类型对象。expires 指向的是一个用于记录键的过期工夫的字典,它的键为 dict 中的数据库键,它的值为这个数据库键的过期工夫戳,这个值以 long long 类型示意。
- RDB 长久化
RDB 长久化(也称作快照长久化)是指将内存中的数据生成快照保留到磁盘外面,保留的文件后缀是 .rdb。rdb 文件是一个通过压缩的二进制文件,当 Redis 重新启动时,能够读取 rdb 快照文件复原数据。RDB 性能最外围的是 rdbSave 和 rdbLoad 两个函数,前者用于生成 RDB 文件并保留到磁盘,而后者则用于将 RDB 文件中的数据从新载入到内存中:
RDB 文件是一个单文件的全量数据,很适宜数据的容灾备份与复原,通过 RDB 文件复原数据库耗时较短,通常 1G 的快照文件载入内存只需 20s 左右。Redis 提供了手动触发保留、主动保留距离两种 RDB 文件的生成形式,上面先介绍 RDB 的创立和载入过程。
2.1. RDB 的创立和载入
Redis 服务器默认是通过 RDB 形式实现长久化的,对应 redis.conf 文件的配置项如下:
RDB 文件的名称
dbfilename dump.rdb
备份 RDB 和 AOF 文件寄存门路
dir /usr/local/var/db/redis/
复制代码 2.1.1. 手动触发保留
Redis 提供了两个用于生成 RDB 文件的命令,一个是 SAVE,另一个是 BGSAVE。而触发 Redis 进行 RDB 备份的形式有两种,一种是通过 SAVE 命令、BGSAVE 命令手动触发快照生成的形式,另一种是配置保留工夫和写入次数,由 Redis 依据条件主动触发保留操作。
- SAVE 命令
SAVE 是一个同步式的命令,它会阻塞 Redis 服务器过程,直到 RDB 文件创建实现为止。在服务器过程阻塞期间,服务器不能解决任何其余命令申请。
客户端命令
127.0.0.1:6379> SAVE
OK
复制代码
服务端日志
6266:M 15 Sep 2019 08:31:01.258 * DB saved on disk
复制代码执行 SAVE 命令后,Redis 在服务端过程(PID 为 6266)执行了 SAVE 操作,这个操作产生期间会始终阻塞 Redis 客户端的申请解决。
- BGSAVE 命令
BGSAVE 是一个异步式的命令,和 SAVE 命令间接阻塞服务器过程的做法不同,BGSAVE 命令会派生出一个子过程,由子过程负责创立 RDB 文件,服务器过程(父过程)持续解决客户的命令。
客户端命令
127.0.0.1:6379> BGSAVE
Background saving started
复制代码
服务端日志
6266:M 15 Sep 2019 08:31:22.914 * Background saving started by pid 6283
6283:C 15 Sep 2019 08:31:22.915 * DB saved on disk
6266:M 15 Sep 2019 08:31:22.934 * Background saving terminated with success
复制代码通过服务端输入的日志,能够发现 Redis 在服务端过程(PID 为 6266)会为 BGSAVE 命令独自创立(fork)一个子过程(PID 为 6283),并由子过程在后盾实现 RDB 的保留过程,在操作实现之后告诉父过程而后退出。在整个过程中,服务器过程只会耗费大量工夫在创立子过程和解决子过程信号量下面,其余工夫都是待命状态。
BGSAVE 是触发 RDB 长久化的支流形式,上面给出 BGSAVE 命令生成快照的流程:
客户端发动 BGSAVE 命令,Redis 主过程判断以后是否存在正在执行备份的子过程,如果存在则间接返回
父过程 fork 一个子过程(fork 的过程中会造成阻塞的状况),这个过程能够应用 info stats 命令查看 latest_fork_usec 选项,查看最近一次 fork 操作耗费的工夫,单位是微秒
父过程 fork 实现之后,则会返回 Background saving started 的信息提醒,此时 fork 阻塞解除
fork 创立的子过程开始依据父过程的内存数据生成长期的快照文件,而后替换原文件
子过程备份结束后向父过程发送实现信息,父过程更新统计信息
- SAVE 和 BGSAVE 的比拟
命令
SAVE
BGSAVE
IO 类型
同步
异步
是否阻塞
全程阻塞
fork 时产生阻塞
复杂度
O(n)
O(n)
长处
不会耗费额定的内存
不阻塞客户端
毛病
阻塞客户端
fork 子过程耗费内存
2.1.2. 主动触发保留
因为 BGSAVE 命令能够在不阻塞服务器过程的状况下执行,所以 Redis 的配置文件 redis.conf 提供了一个 save 选项,让服务器每隔一段时间主动执行一次 BGSAVE 命令。用户能够通过 save 选项设置多个保留条件,只有其中任意一个条件被满足,服务器就会执行 BGSAVE 命令。Redis 配置文件 redis.conf 默认配置了以下 3 个保留条件:
save 900 1
save 300 10
save 60 10000
复制代码那么只有满足以下 3 个条件中的任意一个,BGSAVE 命令就会被主动执行:
服务器在 900 秒之内,对数据库进行了至多 1 次批改。
服务器在 300 秒之内,对数据库进行了至多 10 次批改。
服务器在 60 秒之内,对数据库进行了至多 10000 次批改。
比方通过命令 SET msg “hello” 插入一条键值对,期待 900 秒后 Reids 服务器过程主动触发保留,输入如下:
6266:M 15 Sep 2019 08:46:22.981 * 1 changes in 900 seconds. Saving…
6266:M 15 Sep 2019 08:46:22.986 * Background saving started by pid 6266
6476:C 15 Sep 2019 08:46:23.015 * DB saved on disk
6266:M 15 Sep 2019 08:46:23.096 * Background saving terminated with success
复制代码 Redis 服务器会周期性地操作 serverCron 函数,这个函数每隔 100 毫秒就会执行一次,它的一项工作就是查看 save 选项所设置的保留条件是否满足,如果满足的话,就主动执行 BGSAVE 命令。
2.1.3. 启动主动载入
和应用 SAVE 和 BGSAVE 命令创立 RDB 文件不同,Redis 没有专门提供用于载入 RDB 文件的命令,RDB 文件的载入过程是在 Redis 服务器启动时主动实现的。启动时只有在指定目录检测到 RDB 文件的存在,Redis 就会通过 rdbLoad 函数主动载入 RDB 文件。
上面是 Redis 服务器启动时打印的日志,倒数第 2 条日志是在胜利载入 RDB 文件后打印的。
$ redis-server /usr/local/etc/redis.conf
6266:M 15 Sep 2019 08:30:41.832 # Server initialized
6266:M 15 Sep 2019 08:30:41.833 * DB loaded from disk: 0.001 seconds
6266:M 15 Sep 2019 08:30:41.833 * Ready to accept connections
复制代码因为 AOF 文件属于增量的写入命令备份,RDB 文件属于全量的数据备份,所以更新频率比 RDB 文件的更新频率高。所以如果 Redis 服务器开启了 AOF 长久化性能,那么服务器会优先应用 AOF 文件来还原数据库状态;只有在 AOF 的长久化性能处于敞开状态时,服务器才会应用应用 RDB 文件还原数据库状态。
2.2. RDB 的文件构造
RDB 文件是通过压缩的二进制文件,上面介绍对于 RDB 文件外部结构的一些细节。
2.2.1. 存储门路
SAVE 命令和 BGSAVE 命令都只会备份以后数据库,备份文件名默认为 dump.rdb,可通过配置文件批改备份文件名 dbfilename xxx.rdb。能够通过以下命令查看备份文件目录和 RDB 文件名称:
$ redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> CONFIG GET dir
1) “dir”
2) “/usr/local/var/db/redis”
127.0.0.1:6379> CONFIG GET dbfilename
1) “dbfilename”
2) “dump.rdb”
复制代码 RDB 文件的存储门路既能够在启动前配置,也能够通过命令动静设定。
配置项:通过 dir 配置指定目录,dbfilename 指定文件名
动静指定:Redis 启动后也能够动静批改 RDB 存储门路,在磁盘侵害或空间有余时十分有用,执行命令为:
CONFIG SET dir $newdir
CONFIG SET dbfilename $newFileName
复制代码 2.2.2. 文件格式
RDB 文件有固定的格局要求,它保留的是二进制数据,大体能够分为以下 5 局部:
REDIS:文件头保留的是长为 5 个字节的 REDIS 字符,用于标识以后文件为 RDB 类型
db_version:一个 4 个字节长的整数字符串,用于记录 RDB 文件的版本号
aux:记录着 RDB 文件中元数据信息,蕴含 8 个附加
redis-ver:Redis 实例的版本号
redis-bits:运行 Redis 实例的主机架构,64 位或 32 位
ctime:RDB 创立时的 Unix 工夫戳
used_mem:存储快照时应用的内存大小
repl-stream-db:Redis 服务器的 db 的索引
repl-id:Redis 主实例的 ID(replication id)
repl-offset:Redis 主实例的偏称量(replication offset)
aof-preamble:是否在 AOF 文件头部搁置 RDB 快照(即开启混合长久化)
databases:局部蕴含着零个或者任意多个数据库,以及各个数据库的键值对数据
EOF:是 1 个字节的常量,用于标记 RDB 文件的注释内容完结
check_sum:一个 8 字节长的整数,保留着由后面四个局部计算失去的校验和,用于检测 RDB 文件的完整性
- database
一个 RDB 文件的 databases 局部蕴含着零个或者任意多个数据库(database),而每个非空的 database 都蕴含 SELECTDB、db_number 以及 key_value_pairs 三个局部:
SELECTDB:长度为一个字节的常量,通知用户程序接下来要读取的是一个 db_number
db_number:保留着一个数据库编号。当程序读到 db_number 时,服务器会立刻调用 SELECT 命令切换到对应编号的数据库
key_value_pairs:保留了数据库中的所有键值对数据,包含带过期工夫和不带过期工夫两种类型的键值对
- key_value_pairs
RDB 的 key_value_pairs 局部保留了一个或者多个键值对,如果键值对有过期工夫,过期工夫会被保留在键值对的后面。上面是这两种键值对的内部结构:
EXPIREMENT_MS:长度为一个字节的常量,通知用户程序接下来要读取的是一个以毫秒为单位的过期工夫
ms:一个长度为 8 个字节的整数,记录着键值对的过期工夫,是一个以毫秒为单位的工夫戳
TYPE:记录了 value 的类型,长度为 1 个字节。每个 TYPE 常量都代表了一种对象类型或者底层编码,当服务器读入 RDB 文件中的键值对数据时,程序会依据 TYPE 的值来决定如何读入和解释 value 的数据。它的值定义通常为以下常量之一:
REDIS_RDB_TYPE_STRING:字符串
REDIS_RDB_TYPE_LIST:列表类型
REDIS_RDB_TYPE_SET:汇合类型
REDIS_RDB_TYPE_ZSET:有序汇合
REDIS_RDB_TYPE_HASH:哈希类型
REDIS_RDB_TYPE_LIST_ZIPLIST:列表类型
REDIS_RDB_TYPE_SET_INT_SET:汇合类型
REDIS_RDB_TYPE_ZSET_ZIPLIST:有序汇合
REDIS_RDB_TYPE_HASH_ZIPLIST:哈希类型
key:一个字符串对象,编码格局和 REDIS_RDB_TYPE_STRING 类型的 value 一样
value:取决于 TYPE 的类型,对象类型能够是 string、list、set、zset 和 hash
为了查看 RDB 文件外部的构造,执行以下命令往 Redis 服务器插入 3 条键值对数据:
127.0.0.1:6379> SADD fruits “apple” “banana” “orange”
(integer) 3
127.0.0.1:6379> LPUSH numbers 128 256 512
(integer) 3
127.0.0.1:6379> SET msg “hello”
OK
复制代码执行 SAVE 操作,将 Redis 过程中的数据强制长久化到 dump.rdb 文件中
127.0.0.1:6379> SAVE
OK
复制代码通过 Linux 的 od 命令将二进制文件 dump.rdb 中的数据转换为 ASCII 格局输入,跟后面提到的存储格局大抵是一样的:
$ od -c dump.rdb
0000000 R E D I S 0 0 0 9 372 \t r e d i s
0000020 – v e r 005 5 . 0 . 5 372 \n r e d i
0000040 s – b i t s 300 @ 372 005 c t i m e 200
0000060 200 200 231 ] 372 \b u s e d – m e m 302 200
0000100 \v 020 \0 372 \f a o f – p r e a m b l
0000120 e 300 \0 376 \0 373 003 \0 \0 003 m s g 005 h e
0000140 l l o 016 \a n u m b e r s 001 027 027 \0
0000160 \0 \0 022 \0 \0 \0 003 \0 \0 300 \0 002 004 300 \0 001
0000200 004 300 200 \0 377 002 006 f r u i t s 003 006 o
0000220 r a n g e 005 a p p l e 006 b a n a
0000240 n a 377 214 ک ** 3 366 < r X
0000253
复制代码 2.3. RDB 罕用的配置项
上面是 redis.conf 文件中和 RDB 文件相干的罕用配置项(以及默认值):
save m n:bgsave 主动触发的条件;如果没有 save m n 配置,相当于主动的 RDB 长久化敞开,不过此时仍能够通过其余形式触发。
stop-writes-on-bgsave-error yes:当 bgsave 呈现谬误时,Redis 是否进行执行写命令。如果设置为 yes,则当硬盘呈现问题时,能够及时发现,防止数据的大量失落;如果设置为 no,则 Redis 疏忽 bgsave 的谬误继续执行写命令,当对 Redis 服务器的零碎(尤其是硬盘)应用了监控时,该选项思考设置为 no。
rdbcompression yes:是否开启 RDB 文件压缩。
rdbchecksum yes:是否开启 RDB 文件的校验,在写入文件和读取文件时都起作用。敞开 checksum 在写入文件和启动文件时大概能带来 10% 的性能晋升,然而数据损坏时无奈发现。
dbfilename dump.rdb:设置 RDB 的文件名。
dir ./:设置 RDB 文件和 AOF 文件所在目录。
- AOF 长久化
RDB 长久化是定期把内存中的数据全量写入到文件中,除此之外,RDB 还提供了基于 AOF(Append Only File)的长久化性能。AOF 会把 Redis 服务器每次执行的写命令记录到一个日志文件中,当服务器重启时再次执行 AOF 文件中的命令来复原数据。
AOF 的次要作用是解决了数据长久化的实时性,目前曾经成为了 Redis 长久化的支流形式。
3.1. AOF 的创立和载入
默认状况下 AOF 性能是敞开的,Redis 只会通过 RDB 实现数据长久化的。开启 AOF 性能须要 redis.conf 文件中将 appendonly 配置项批改为 yes,这样在开启 AOF 长久化性能的同时,将基于 RDB 的快照长久化置于低优先级。批改 redis.conf 如下:
此选项为 AOF 性能的开关,默认为 no,通过 yes 来开启 aof 性能
appendonly yes
指定 AOF 文件名称
appendfilename appendonly.aof
备份 RDB 和 AOF 文件寄存门路
dir /usr/local/var/db/redis/
复制代码 3.1.1. AOF 的创立
重启 Redis 服务器过程当前,dir 目录下会生成一个 appendonly.aof 文件,因为此时服务器未执行任何写指令,因而 AOF 文件是空的。执行以下命令写入几条测试数据:
127.0.0.1:6379> SADD fruits “apple” “banana” “orange”
(integer) 3
127.0.0.1:6379> LPUSH numbers 128 256 512
(integer) 3
127.0.0.1:6379> SET msg “hello”
OK
复制代码 AOF 文件是纯文本格式的,上述写命令按程序被写入了 appendonly.aof 文件(省掉换行符 ‘\r\n’):
/usr/local/var/db/redis$ cat appendonly.aof
*2 $6 SELECT $1 0
*5 $4 SADD $6 fruits $5 apple $6 banana $6 orange
*5 $5 LPUSH $7 numbers $3 128 $3 256 $3 512
*3 $3 SET $3 msg $5 hello
复制代码 RDB 长久化的形式是将 apple、banana、orange 的键值对数据保留为 RDB 的二进制文件,而 AOF 是通过把 Redis 服务器执行的 SADD、LPUSH、SET 等命令保留到 AOF 的文本文件中。下图是 AOF 文件外部的结构图:
3.1.2. AOF 的载入
再次重启 Redis 服务器过程,察看启动日志会发现 Redis 会通过 AOF 文件加载数据:
52580:M 15 Sep 2019 16:09:47.015 # Server initialized
52580:M 15 Sep 2019 16:09:47.015 * DB loaded from append only file: 0.001 seconds
52580:M 15 Sep 2019 16:09:47.015 * Ready to accept connections
复制代码通过命令读取 AOF 文件还原的键值对数据:
127.0.0.1:6379> SMEMBERS fruits
1) “apple”
2) “orange”
3) “banana”
127.0.0.1:6379> LRANGE numbers 0 -1
1) “512”
2) “256”
3) “128”
127.0.0.1:6379> GET msg
“hello”
复制代码 3.2. AOF 的执行流程
AOF 不须要设置任何触发条件,对 Redis 服务器的所有写命令都会自动记录到 AOF 文件中,上面介绍 AOF 长久化的执行流程。
AOF 文件的写入流程能够分为以下 3 个步骤:
命令追加(append):将 Redis 执行的写命令追加到 AOF 的缓冲区 aof_buf
文件写入(write)和文件同步(fsync):AOF 依据对应的策略将 aof_buf 的数据同步到硬盘
文件重写(rewrite):定期对 AOF 进行重写,从而实现对写命令的压缩。
3.2.1. 命令追加
Redis 应用单线程解决客户端命令,为了防止每次有写命令就间接写入磁盘,导致磁盘 IO 成为 Redis 的性能瓶颈,Redis 会先把执行的写命令追加(append)到一个 aof_buf 缓冲区,而不是间接写入文件。
命令追加的格局是 Redis 命令申请的协定格局,它是一种纯文本格式,具备兼容性好、可读性强、容易解决、操作简略防止二次开销等长处。在 AOF 文件中,除了用于指定数据库的 select 命令(比方:select 0 为选中 0 号数据库)是由 Redis 增加的,其余都是客户端发送来的写命令。
3.2.2. 文件写入和文件同步
Redis 提供了多种 AOF 缓存区的文件同步策略,相干策略波及到操作系统的 write() 函数和 fsync() 函数,阐明如下:
- write()
为了进步文件的写入效率,当用户调用 write 函数将数据写入文件时,操作系统会先把数据写入到一个内存缓冲区里,当缓冲区被填满或超过了指定时限后,才真正将缓冲区的数据写入到磁盘里。 - fsync()
尽管操作系统底层对 write() 函数进行了优化,但也带来了平安问题。如果宕机内存缓冲区中的数据会失落,因而零碎同时提供了同步函数 fsync(),强制操作系统立即将缓冲区中的数据写入到磁盘中,从而保障了数据长久化。
Redis 提供了 appendfsync 配置项来管制 AOF 缓存区的文件同步策略,appendfsync 可配置以下三种策略:
appendfsync always:每执行一次命令保留一次
命令写入 aof_buf 缓冲区后立刻调用零碎 fsync 函数同步到 AOF 文件,fsync 操作实现后线程返回,整个过程是阻塞的。这种状况下,每次有写命令都要同步到 AOF 文件,硬盘 IO 成为性能瓶颈,Redis 只能反对大概几百 TPS 写入,重大升高了 Redis 的性能。
appendfsync no:不保留
命令写入 aof_buf 缓冲区后调用零碎 write 操作,不对 AOF 文件做 fsync 同步;同步由操作系统负责,通常同步周期为 30 秒。这种状况下,文件同步的工夫不可控,且缓冲区中沉积的数据会很多,数据安全性无奈保障。
appendfsync everysec:每秒钟保留一次
命令写入 aof_buf 缓冲区后调用零碎 write 操作,write 实现后线程立即返回,fsync 同步文件操作由独自的过程每秒调用一次。everysec 是前述两种策略的折中,是性能和数据安全性的均衡,因而也是 Redis 的默认配置,也是比拟推崇的配置选项。
文件同步策略
write 阻塞
fsync 阻塞
宕机时的数据失落量
always
阻塞
阻塞
最多只失落一个命令的数据
no
阻塞
不阻塞
操作系统最初一次对 AOF 文件 fsync 后的数据
everysec
阻塞
不阻塞
个别不超过 1 秒钟的数据
3.2.3. 文件重写
随着命令一直写入 AOF,文件会越来越大,导致文件占用空间变大,数据恢复工夫变长。为了解决这个问题,Redis 引入了重写机制来对 AOF 文件中的写命令进行合并,进一步压缩文件体积。
AOF 文件重写指的是把 Redis 过程内的数据转化为写命令,同步到新的 AOF 文件中,而后应用新的 AOF 文件笼罩旧的 AOF 文件,这个过程不对旧的 AOF 文件的进行任何读写操作。
- 触发机制
AOF 重写过程提供了手动触发和主动触发两种机制:
手动触发:间接调用 bgrewriteaof 命令,该命令的执行与 bgsave 有些相似,都是 fork 子过程进行具体的工作,且都只有在 fork 时会阻塞
主动触发:依据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 配置项,以及 aof_current_size 和 aof_base_size 的状态确定触发机会
auto-aof-rewrite-min-size:执行 AOF 重写时,文件的最小体积,默认值为 64MB
auto-aof-rewrite-percentage:执行 AOF 重写时,以后 AOF 大小(aof_current_size)和上一次重写时 AOF 大小(aof_base_size)的比值
- 重写流程
上面以手动触发 AOF 重写为例,当 bgrewriteaof 命令被执行时,AOF 文件重写的流程如下:
客户端通过 bgrewriteaof 命令对 Redis 主过程发动 AOF 重写申请
以后不存在正在执行 bgsave/bgrewriteaof 的子过程时,Redis 主过程通过 fork 操作创立子过程,这个过程主过程是阻塞的。如果发现 bgrewriteaof 子过程间接返回;如果发现 bgsave 子过程则等 bgsave 执行实现后再执行 fork 操作
主过程的 fork 操作实现后,持续解决其余命令,把新的写命令同时追加到 aof_buf 和 aof_rewrite_buf 缓冲区中
在文件重写实现之前,主过程会持续把写命令追加到 aof_buf 缓冲区,依据 appendfsync 策略同步到旧的 AOF 文件,这样能够防止 AOF 重写失败造成数据失落,保障原有的 AOF 文件的正确性
因为 fork 操作使用写时复制技术,子过程只能共享 fork 操作时的内存数据,主过程会把新命令追加到一个 aof_rewrite_buf 缓冲区中,防止 AOF 重写时失落这部分数据
子过程读取 Redis 过程中的数据快照,生成写入命令并依照命令合并规定批量写入到新的 AOF 文件
子过程写完新的 AOF 文件后,向主过程发信号,主过程更新统计信息,具体能够通过 info persistence 查看
主过程承受到子过程的信号当前,将 aof_rewrite_buf 缓冲区中的写命令追加到新的 AOF 文件
主过程应用新的 AOF 文件替换旧的 AOF 文件,AOF 重写过程实现
- 压缩机制
文件重写之所以可能压缩 AOF 文件的大小,起因在于以下几方面:
过期的数据不再写入 AOF 文件
有效的命令不再写入 AOF 文件。比方:反复为数据设值(set mykey v1, set mykey v2)、删除键值对数据(sadd myset v1, del myset)等等
多条命令能够合并为单个。比方:sadd myset v1, sadd myset v2, sadd myset v3 能够合并为 sadd myset v1 v2 v3。不过为了避免单条命令过大造成客户端缓冲区溢出,对于 list、set、hash、zset 类型的 key,并不一定只应用单条命令,而是以某个 Redis 定义的一个常量为界,将命令拆分为多条
3.3. AOF 罕用的配置项
上面是 redis.conf 文件中和 AOF 文件相干的罕用配置项(以及默认值):
appendonly no:是否开启 AOF 长久化性能
appendfilename “appendonly.aof”:AOF 文件的名称
dir ./:RDB 文件和 AOF 文件所在目录
appendfsync everysec:fsync 长久化策略
no-appendfsync-on-rewrite no:重写 AOF 文件期间是否禁止 fsync 操作。如果开启该选项,能够加重文件重写时 CPU 和磁盘的负载(尤其是磁盘),然而可能会失落 AOF 重写期间的数据,须要在负载和安全性之间进行均衡
auto-aof-rewrite-percentage 100:AOF 文件重写触发条件之一
auto-aof-rewrite-min-size 64mb:AOF 文件重写触发条件之一
aof-load-truncated yes:如果 AOF 文件结尾损坏,Redis 服务器在启动时是否仍载入 AOF 文件
- 数据恢复机制
后面提到当 AOF 长久化性能开启时,Redis 服务器启动时优先执行 AOF 文件的命令复原数据,只有当 AOF 性能敞开时,才会优先载入 RDB 快照的文件数据。
当 AOF 性能敞开,且 RDB 长久化开启时,Redis 服务器启动日志:
6266:M 15 Sep 2019 08:30:41.832 # Server initialized
6266:M 15 Sep 2019 08:30:41.833 * DB loaded from disk: 0.001 seconds
6266:M 15 Sep 2019 08:30:41.833 * Ready to accept connections
复制代码
当 AOF 性能开启,且 AOF 文件存在时,Redis 服务器启动日志:
9447:M 15 Sep 2019 23:01:46.601 # Server initialized
9447:M 15 Sep 2019 23:01:46.602 * DB loaded from append only file: 0.001 seconds
9447:M 15 Sep 2019 23:01:46.602 * Ready to accept connections
复制代码
当 AOF 性能开启,且 AOF 文件不存在时,即便 RDB 文件存在也不会加载,Redis 服务器启动日志:
9326:M 15 Sep 2019 22:49:24.203 # Server initialized
9326:M 15 Sep 2019 22:49:24.203 * Ready to accept connections
复制代码 5. RDB 和 AOF 比照
长久化机制
RDB
AOF
启动优先级
低
高
磁盘文件体积
小
大
数据还原速度
快
慢
数据安全性
容易失落数据
依据策略决定
操作轻重级别
重
轻
5.1. RDB 的优缺点
5.1.1. 长处
RDB 是一个压缩过的十分紧凑的文件,保留着某个工夫点的数据集,适宜做数据的备份、劫难复原
能够最大化 Redis 的性能,在保留 RDB 文件,服务器过程只需 fork 一个子过程来实现 RDB 文件的创立,父过程不须要做 IO 操作
与 AOF 长久化形式相比,复原大数据集的时候会更快
5.1.2. 毛病
RDB 的数据安全性是不如 AOF 的,保留整个数据集是个重量级的过程,依据配置可能要几分钟才进行一次长久化,如果服务器宕机,那么就可能失落几分钟的数据
Redis 数据集较大时,fork 的子过程要实现快照会比拟消耗 CPU 和工夫
5.2. AOF 的优缺点
5.2.1. 长处
数据更残缺,安全性更高,秒级数据失落(取决于 fsync 策略,如果是 everysec,最多失落 1 秒的数据)
AOF 文件是一个只进行追加的命令文件,且写入操作是以 Redis 协定的格局保留的,内容是可读的,适宜误删紧急复原
5.2.2. 毛病
对于雷同的数据集,AOF 文件的体积要远远大于 RDB 文件,数据恢复也会比较慢
依据所应用的 fsync 策略,AOF 的速度可能会慢于 RDB。不过在个别状况下,每秒 fsync 的性能仍然十分高
-
RDB-AOF 混合长久化
在重启 Redis 服务器时,个别很少应用 RDB 快照文件来复原内存状态,因为会失落大量数据。更多的是应用 AOF 文件进行命令重放,然而执行 AOF 命令性能绝对 RDB 来说要慢很多。这样在 Redis 数据很大的状况下,启动须要耗费大量的工夫。
鉴于 RDB 快照可能会造成数据失落,AOF 指令复原数据慢,Redis 4.0 版本提供了一套基于 AOF-RDB 的混合长久化机制,保留了两种长久化机制的长处。这样重写的 AOF 文件由两部份组成,一部分是 RDB 格局的头部数据,另一部分是 AOF 格局的尾部指令。
Redis 4.0 版本的混合长久化性能默认是敞开的,通过配置 aof-use-rdb-preamble 为 yes 开启此性能:开启 AOF-RDB 混合长久化机制
aof-use-rdb-preamble yes
复制代码查看 Redis 服务器是否开启混合长久化性能:
127.0.0.1:6379> CONFIG GET aof-use-rdb-preamble
1) “aof-use-rdb-preamble”
2) “yes”
复制代码如图所示,将 RDB 数据文件的内容和增量的 AOF 命令文件存在一起。这里的 AOF 命令不再是全量的命令,而是自长久化开始到长久化完结的这段时间服务器过程执行的增量 AOF 命令,通常这部分 AOF 命令很小。
在 Redis 服务器重启的时候,能够事后加载 AOF 文件头部全量的 RDB 数据,而后再重放 AOF 文件尾部增量的 AOF 命令,从而大大减少了重启过程中数据还原的工夫。
- 长久化策略抉择
7.1. RDB 和 AOF 性能开销
在介绍长久化策略之前,首先要明确无论是 RDB 还是 AOF 形式,开启长久化都是会造成性能开销的。
RDB 长久化:
BGSAVE 命令在进行 fork 操作时,Redis 服务器主过程会产生阻塞
Redis 子过程向磁盘写入数据也会带来 IO 压力
AOF 长久化:
向磁盘写入数据的频率大大提高,IO 压力更大,甚至可能造成 AOF 追加阻塞问题
AOF 文件重写与 RDB 的 BGSAVE 过程相似,存在父过程 fork 时的阻塞和子过程的 IO 压力问题
相对来说,因为 AOF 向磁盘中写入数据的频率更高,因而对 Redis 服务器主过程性能的影响会更大。
7.2. 长久化策略
在理论生产环境中,依据数据量、利用对数据的平安要求、估算限度等不同状况,会有各种各样的长久化策略。
齐全不应用任何长久化性能
应用 RDB 或 AOF 其中一种
同时开启 RDB 和 AOF 长久化
对于分布式环境,长久化的抉择必须与 Redis 的主从策略一起思考,因为主从复制与长久化同样具备数据备份的性能,而且主节点(Master Node)和从节点(Slave Node)能够独立抉择长久化计划。
上面分场景来探讨长久化策略的抉择,上面的探讨也只是作为参考,理论计划可能更简单更具多样性。
7.2.1. 数据库缓存
如果 Redis 中的数据齐全抛弃也没有关系(如 Redis 齐全用作 DB 层数据的缓存),那么无论是单机,还是主从架构,都能够不进行任何长久化。
7.2.2. 单机环境
在单机环境下,如果能够承受十几分钟或更多的数据失落,RDB 计划对 Redis 的性能更加无利;如果只能承受秒级别的数据失落,抉择 AOF 计划更适合。
7.2.3. 主从部署
在少数状况下,Redis 都会配置主从部署机制。从节点(slave)既能够实现数据的热备,也能够进行读写分担 Redis 读申请,以及在主节点(master)宕机后的顶替作用。
在这种状况下,一种可行的做法如下:
master:齐全敞开长久化(包含 RDB 和 AOF 性能),这样能够让主节点的性能达到最好
slave:敞开 RDB 性能,开启 AOF 性能(如果对数据安全要求不高,开启 RDB 敞开 AOF 也能够)。定时对长久化文件进行备份(如备份到其余文件夹,并标记好备份的工夫)。而后敞开 AOF 的主动重写性能,而后增加定时工作,在每天 Redis 服务器闲时(如凌晨 12 点)调用 bgrewriteaof 手动重写。
为什么开启了主从复制,能够实现数据的热备份,还须要设置长久化呢?因为在一些非凡状况下,主从复制依然不足以保证数据的平安,例如:
master 和 slave 同时进行:如果 master 节点和 slave 节点位于同一个机房,则一次停电事变就可能导致 master 和 slave 机器同时关机,Redis 服务器过程进行。如果没有长久化,则面临的是数据的齐全失落。
master 重启:如果 master 节点因为故障宕机,并且零碎中有主动拉起机制(即检测到服务进行后重启该服务)将 master 节点主动重启。
因为没有长久化文件,那么 master 重启后数据是空的,slave 同步数据也变成了空的
如果 master 和 slave 节点都没有开启长久化,同样会引发数据的齐全失落
7.2.4. 异地备灾
后面的几种长久化策略,针对的都是个别的系统故障,如过程异样退出、宕机、断电等,这些故障不会损坏硬盘。然而对于一些可能导致硬盘损坏的劫难状况,如火灾地震,就须要进行异地灾备。
单机环境:能够定时将 RDB 文件或重写后的 AOF 文件,通过 scp 命令拷贝到近程机器,如阿里云、AWS 等
主从部署,能够定时在 master 节点上执行 BGSAVE 操作,而后将 RDB 文件拷贝到近程机器,或者在 slave 节点上执行 bgrewriteaof 命令重写 AOF 文件后,将 AOF 文件拷贝到近程机器上。
因为 RDB 文件文件小、复原速度快,劫难复原个别采纳 RDB 形式;异地备份的频率依据数据安全性的须要及其它条件来确定,但最好不要低于一天一次。
小结
本文次要开篇介绍了 Redis 服务器的数据库构造,进一步介绍了 Redis 提供的几种长久化机制,包含基于数据快照的 RDB 全量长久化、基于命令追加的 AOF 增量长久化以及 Redis 4.0 反对的混合长久化。对于 RDB 的长久化形式,给出了 RDB 快照的创立和还原过程,RDB 的文件构造以及相干配置项。对于 AOF 的长久化形式,给出了 AOF 日志的创立和还原过程,AOF 的执行流程,AOF 文件外部的格局以及相干配置项。在文章结尾剖析了 RDB 和 AOF 形式各自的优缺点,性能开销,以及在单机环境、主从部署、异地备灾场景下的长久化策略。