redis作为一个高性能的key-value数据库,很大程度依赖于其基于内存的数据操作。正因其数据存放在内存中,若遇到服务异常退出、宕机等情况,无法将其数据进行恢复。好在redis为我们提供了数据持久化方案,以解决上述问题。
redis在4.0版本之前提供两种数据持久化方案,分别是RDB(全量)和AOF(增量),4.0之后提供了RDB-AOF混合持久化方案,充分利用了AOF和RDB各自优势。本文将就这三种方案从使用到底层原理进行介绍。
1. RDB
RDB是一种全量持久化方案,启用RDB后,redis会在程序满足用户配置的规则时,将内存中的数据全量写入文件系统,已达到持久化的目的。
1.1 RDB配置
redis持久化一般通过配置文件来进行配置,默认情况下(redis5.0.8),redis将内存数据保存到dump.rdb的二进制文件中,并且当每900秒且1次写操作,或每300秒10次写操作,或每60秒10000次写操作均会触发RDB持久化。我可以通过修改配置文件来修改上述持久化规则。
#持久化存储文件
dbfilename dump.rdb
#每900秒且1次写操作
save 900 1
#每300秒10次写操作
save 300 10
#每60秒10000次写操作
save 60 10000
同个配置文件中可以通过多条save命令来达到满足指定条件中任一规则时,触发持久化。即当存在多个save命令,他们为“或”的关系。若不想开启RDB持久化,只需要将所有的save保存策略注释掉即可。
1.2 手动生成RDB快照
除了通过配置文件配置redis自动生成RDB快照之外,我们还可以通过客户端运行save或bgsave命令,来手动让redis生成RDB快照文件。每次命令执行后redis会将所有内存数据生成一个RDB格式文件,并覆盖原有的RDB快照文件。
save是同步命令,bgsave是异步命令,bgsave会从redis主进程fork出一个子进程,专门用来生成RDB快照文件。save与bgsave对比如下:
命令 | save | bgsave |
---|---|---|
IO类型 | 同步 | 异步 |
是否阻塞redis其它命令 | 是 | 否 |
复杂度 | O(n) | O(n) |
优点 | 不会消耗额外内存 | 不阻塞客户端命令 |
缺点 | 阻塞客户端命令 | 需要fork子进程 |
注意:通过1.1节配置文件来自动生成RDB文件的方式,redis使用的是bgsave的方式。
2. AOF
AOF(append only file)类似于Mysql的binlog,是一种增量持久化方案。AOF方案下,redis会将用户操作的指令(只记录写命令)记录到AOF文件。
2.1 AOF配置
默认情况下,redis不会开启AOF持久化方案,我们可以通过在配置文件中添加以下配置开启AOF:
#启用AOF持久化
appendonly yes
#配置AOF存储路径
appendfilename "appendonly.aof"
AOF有三种规则可供选择,分别为always、everysec、no,用来设置redis将命令写入AOF文件的时机。
#有新命令立即追加到 AOF 文件,速度慢,但数据安全性最高
appendfsync always
#每秒同步一次命令到文件中,速度较快(和使用 RDB 持久化差不多),在故障发生时最多只会丢失 1 秒钟的数据
appendfsync everysec
#从不fsync,由操作系统自动调度刷磁盘,速度最快,但安全性最低。
appendfsync no
推荐在对数据安全性要求不高的场景下,使用everysec的fsync策略,这种策略可以同时兼顾性能和安全性。
2.2 AOF原理
如本节开头介绍,redis会在新的写命令被执行时记录该命令,并按照用户配置的策略将命令以RESP格式(redis客户端与服务端通信协议格式)写入文件系统。整个过程大致如下图所示:
2.2.1 RESP
RESP协议以 “*数字”标记为开头,表明指令共有多少段。每一段又以“$数字”标记开头,表明该段的内容长度,另外每个标记和内容均以过行符作为分隔。如下为执行“set everlin 1”命令的AOF文件内容:
*3
$3
set
$7
everlin
$1
1
2.2.1 AOF重写
随着时间推移,AOF中记录的指令会越来越多,文件占用空间越来越大,当系统重启去加载AOF时,也会变得越慢。为了解决这一问题,redis提供了AOF重写的功能。AOF重写的理论依据是,我们会对某个key进行多次写操作,而我们很容易找到一个命令,对这个key执行后的结果,与前边多次操作的结果一致。
例如执行以下命令:
incr everlin
incr everlin
incr everlin
在没有进行AOF重写时,AOF文件中的内容如下:
*2
$4
incr
$7
everlin
*2
$4
incr
$7
everlin
*2
$4
incr
$7
everlin
在redis进行AOF重写后,AOF文件内容变为:
*3
$3
set
$7
everlin
$1
4
需要注意,重写AOF文件的操作,redis并不是读取旧的AOF文件后进行成功学,而是将整个内存中的数据库内容用命令的方式重写了一个新的AOF文件,这点和快照有点类似。此外,在redis4.0之后,若开启了混合持久化方案,那么AOF重写的数据将不再以RESP格式写入,而是以RDB二进制格式写入AOF文件中。
AOF重写也需要配置策略才能触发,如下两个配置可以控制AOF自动重写频率
#AOF文件至少要达到64M才进行重写
auto-aof-rewrite-min-size 64mb
#AOF文件自上一次重写后文件大小增长了100%,再次触发重写
auto-aof-rewrite-percentage 100
我们还可以通过在客户端使用bgrewriteaof命令,来对AOF进行手动重写。redis通过fork一个子进程进行AOF重写,因此,AOF重写不会对redis正常命令处理产生太大影响。
3. RDB VS AOF
通过1、2节的介绍,我们整理出RDB与AOF持久化方案的对比如下:
特点 | RDB | AOF |
---|---|---|
文件体积 | 小 | 大 |
性能影响 | 低 | 由同步策略决定(参见2.1) |
数据安全 | 容易丢数据 | 由同步策略决定(参见2.1) |
恢复速度 | 快 | 慢 |
3.1 同时开启RDB和AOF
当同时开启RDB持久化和AOF持久化时,两种方案都会被redis启用。但当redis重启时,若同时存在RDB文件及AOF文件,redis会优先选择AOF文件进行数据恢复。因为AOF方案能保证数据更接近与上次redis停止时的数据。
4. 混合持久化方案
前一节我们对比了RDB和AOF各自的优缺点,RDB占用存储空间小,恢复速度快,AOF数据安全性更高,那有没有方法可以发挥两者的优势,取长补短呢。答案是有的,redis4.0之后提供了混合持久化方案,该方案基本与AOF方案一致,不同点在于,当AOF重写时,以RDB格式对AOF进行重写。此时AOF的文件结构如下:
混合持久化方案提供了一种简单粗暴的方式,取长补短,既保留了RDB文件体积小,恢复速度快的优势,又解决了RDB持久化容易丢失数据的问题。我们可以在配置文件中使用以下配置,启用混合持久化:
aof-use-rdb-preamble yes
5. 小结
本文分别介绍了RDB,AOF,混合持久化3种持久化方案:RDB文件体积小,恢复速度快,但容易丢失数据;AOF可根据fsync策略提供不同的数据安全等级,可以做到数据不丢失,但其恢复速度慢,redis提供了AOF重写来压缩其文件体积,提高恢复速度;混合持久化结合了RDB和AOF的优势,在AOF重写时以RDB格式将数据置于AOF文件开头,大大提高了AOF的恢复速度,同时也保留了其数据安全性高的优势。建议在需要进行持久化的场景下,选用混合持久化方案。