Redis 数据持久化

696 阅读12分钟

Redis通常用来作为缓存数据库,数据存放在内存中就有可能发生断电数据丢失的问题。幸好,Redis支持数据持久化,可以将内存中的数据同步到磁盘中。

持久化方式

Redis的持久化策略有以下两种:

  • RDB(Redis Database Backup file):以快照形式把内存中的形式保存到一个dump文件中,定时保存
  • AOF(append-only file):把每条的命令,以append-only的模式写入到一个日志文件中,因为是只追加模式,没有任何磁盘寻址的开销,所以很快就能写入。

两种方式都支持将内存数据持久化,但是RDB文件,备份较为耗时,所以不能每时每刻都在进行快照,但是优点数据恢复起来会更快。所以常用来做冷备份,而AOF备份几乎不耗时,但是恢复较为耗时,所以更适合做热备份.

RDB快照持久化

RDB持久化是通过快照的方式,即在指定的时间间隔内将内存中的数据集快照写入磁盘。在创建快照之后,用户可以备份该快照,可以将快照复制到其他服务器以创建相同数据的服务器副本,或者在重启服务器后恢复数据。

参考自书本Redis实战 4.1.1 快照持久化 [62-63]

  • 先来看看RDB的命令
// 多久执行一次快照持久化   save 60 10000 1分钟之内,如果1万个key被修改,则发起快照保存;
save <seconds> <changes>  
// 创建快速失败后是否仍然继续执行写命令    
stop-write-on-bagsave-error no
// 是否对快照进行压缩
rbdcompression yes
// 如何命名硬盘上的快照文件
dbfilename dump.rdb

快照执行时发生了什么

当 Redis 需要做持久化时,Redis 会 fork 一个子进程,子进程将数据写到磁盘上一个临时 RDB 文件中。当子进程完成写临时文件后,将原来的 RDB 替换掉.这样的好处就是可以 copy-on-write。

生成的文件是一个二进制的文件,默认文件名为dump.rdb.

快照的触发时机

  • 执行savebgsave命令

  • 配置文件设置save <seconds> <changes>规则,自动间隔性执行bgsave命令

  • 主从复制时,从库全量复制同步主库数据,主库会执行bgsave

  • 执行flushall命令清空服务器数据

  • 执行shutdown命令关闭Redis时,会执行save命令

save命令和bgsave命令

save命令会在快照创建完毕之前不再响应任何其他命令。所以这个命令并不常用

bgsave命令会fork创建一个子进程,然后该子进程负责创建RDB文件,服务器进程会继续处理请求命令。

bgsave命令执行流程

fork()是由操作系统提供的函数,作用是创建当前进程的一个副本作为子进程


大数据情况下的RDB

当Redis存储的数据量只有几GB时,使用RDB快照来保存数据是非常快,只有1秒左右。但是如果,内存中的有海量数据时,fork出来的子进程进行持久化时需要不小的时间,如果自动触发规则设置不好,将会导致频繁的创建子进程进行数据持久化,从而导致系统长时间地停顿,Redis性能降低。所以,海量数据时,RDB自动触发规则只能降低触发的门槛,所以会最终会导致。

海量数据进行RDB时,会丢失一定时间的数据.

AOF持久化

除了RDB持久化,Redis还提供了AOF(Append Only File)持久化功能,AOF持久化会把被执行的写命令写到AOF文件的末尾,记录数据的变化。默认情况下,Redis是没有开启AOF持久化的,开启后,每执行一条更改Redis数据的命令,都会把该命令追加到AOF文件中,这是会降低Redis的性能,但大部分情况下这个影响是能够接受的,另外使用较快的硬盘可以提高AOF的性能

可以通过配置redis.conf文件开启AOF持久化,关于AOF的配置如下:

# appendonly参数开启AOF持久化
appendonly no

# AOF持久化的文件名,默认是appendonly.aof
appendfilename "appendonly.aof"

# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的
dir ./

# 同步策略
# appendfsync always
appendfsync everysec
# appendfsync no

# aof重写期间是否同步
no-appendfsync-on-rewrite no

# 重写触发配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 加载aof出错如何处理
aof-load-truncated yes

# 文件重写策略
aof-rewrite-incremental-fsync yes

AOF的实现

AOF需要记录Redis的每个写命令,步骤为:命令追加(append)、文件写入(write)和文件同步(sync)

命令追加(append)

开启AOF持久化功能后,服务器每执行一个写命令,都会把该命令以协议格式先追加到aof_buf缓存区的末尾,而不是直接写入文件,避免每次有命令都直接写入硬盘,减少硬盘IO次数*

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

对于何时把aof_buf缓冲区的内容写入保存在AOF文件中,Redis提供了多种策略

  • appendfsync always:将aof_buf缓冲区的所有内容写入并同步到AOF文件,每个写命令同步写入磁盘

  • appendfsync everysec:将aof_buf缓存区的内容写入AOF文件,每秒同步一次,该操作由一个线程专门负责

  • appendfsync no:将aof_buf缓存区的内容写入AOF文件,什么时候同步由操作系统来决定

关于AOF的同步策略是涉及到操作系统的write函数和fsync函数的,在《Redis设计与实现》中是这样说明的

从上面的介绍我们知道,我们写入的数据,操作系统并不一定会马上同步到磁盘,所以Redis才提供了appendfsync的选项配置。当该选项时为always时,数据安全性是最高的,但是会对磁盘进行大量的写入,Redis处理命令的速度会受到磁盘性能的限制;appendfsync everysec选项则兼顾了数据安全和写入性能,以每秒一次的频率同步AOF文件,即便出现系统崩溃,最多只会丢失一秒内产生的数据;如果是appendfsync no选项,Redis不会对AOF文件执行同步操作,而是有操作系统决定何时同步,不会对Redis的性能带来影响,但假如系统崩溃,可能会丢失不定数量的数据

AOF重写(rewrite)

来看看redis官网的一个例子:

例如,如果您要增加一个计数器100次,最终将在数据集中包含最终值的键只有一个,而在AOF中却包含100个条目。

所以AOF的文件体积如此长时间积累,将会非常臃肿。需要措施减少这些多余的命令。

我们先来看看AOF文件中存储的内容是啥,先执行两个写操作

127.0.0.1:6379set s1 hello
OK
127.0.0.1:6379set s2 world
OK

然后我们打开appendonly.aof文件,可以看到如下内容

*3
$3
set
$2
s1
$5
hello
*3
$3
set
$2
s2
$5
world

tips:该命令格式为Redis的序列化协议(RESP)。*3代表这个命令有三个参数,$3表示该参数长度为3

随着时间的推移,Redis执行的写命令会越来越多,AOF文件也会越来越大,过大的AOF文件可能会对Redis服务器造成影响,如果使用AOF文件来进行数据还原所需时间也会越长

时间长了,AOF文件中通常会有一些冗余命令,比如:过期数据的命令、无效的命令(重复设置、删除)、多个命令可合并为一个命令(批处理命令)。所以AOF文件是有精简压缩的空间的

AOF重写的目的就是减小AOF文件的体积,不过值得注意的是:AOF文件重写并不需要对现有的AOF文件进行任何读取、分享和写入操作,而是通过读取服务器当前的数据库状态来实现的

文件重写可分为手动触发和自动触发,手动触发执行bgrewriteaof命令,该命令的执行跟bgsave触发快照时类似的,都是先fork一个子进程做具体的工作

127.0.0.1:6379bgrewriteaof
Background append only file rewriting started

自动触发会根据auto-aof-rewrite-percentageauto-aof-rewrite-min-size 64mb配置来自动执行bgrewriteaof命令

# 表示当AOF文件的体积大于64MB,且AOF文件的体积比上一次重写后的体积大了一倍(100%)时,会执行`bgrewriteaof`命令
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

下面看一下执行bgrewriteaof命令,重写的流程

AOF流程

  • 重写会有大量的写入操作,所以服务器进程会fork一个子进程来创建一个新的AOF文件

  • 在重写期间,服务器进程继续处理命令请求,如果有写入的命令,追加到aof_buf的同时,还会追加到aof_rewrite_bufAOF重写缓冲区

  • 当子进程完成重写之后,会给父进程一个信号,然后父进程会把AOF重写缓冲区的内容写进新的AOF临时文件中,再对新的AOF文件改名完成替换,这样可以保证新的AOF文件与当前数据库数据的一致性

数据恢复

Redis4.0开始支持RDB和AOF的混合持久化(可以通过配置项 aof-use-rdb-preamble 开启)

如果是redis进程挂掉,那么重启redis进程即可,直接基于AOF日志文件恢复数据

如果是redis进程所在机器挂掉,那么重启机器后,尝试重启redis进程,尝试直接基于AOF日志文件进行数据恢复,如果AOF文件破损,那么用redis-check-aof fix命令修复

如果没有AOF文件,会去加载RDB文件

如果redis当前最新的AOF和RDB文件出现了丢失/损坏,那么可以尝试基于该机器上当前的某个最新的RDB数据副本进行数据恢复

RDB和AOF优缺点

看到这里,知道AOF和RDB的本质就可以来说说,他们各自的优缺点了。

如果同时启用了AOF和RDB持久性,并且Redis重新启动,则AOF文件将用于重建原始数据集,因为它可以保证是最完整的。

关于RDB和AOF的优缺点,官网上面也给了比较详细的说明redis.io/topics/pers…

RDB AOF
数据完整性 一般,大数据量的备份通常会丢失数据 好,可以做到只丢秒级的数据
备份耗时 一般,备份相对需要消耗较长时间 好,几乎不耗时
备份文件大小 好,备份文件较小,另外还支持压缩 一般,文件体积较大
数据恢复速度 快,数据恢复,直接把数据导入内存即可 较慢,数据恢复相当于重新执行一遍命令

下面是更为详尽的解释。

RDB

优点:

  • RDB快照是一个压缩过的非常紧凑的二进制文件,保存着某个时间点的数据集,适合进行网络传输,进行异地容灾
  • 可以最大化Redis的性能,在保存RDB文件,服务器进程只需fork一个子进程来完成RDB文件的创建,父进程不需要做IO操作
  • 与AOF相比,恢复大数据集的时候会更快

缺点:

  • RDB的数据安全性是不如AOF的,保存整个数据集的过程是比繁重的,根据配置可能要几分钟才快照一次,如果服务器宕机,那么就可能丢失几分钟的数据
  • Redis数据集较大时,fork的子进程要完成快照会比较耗CPU、耗时

AOF

优点:

  • 数据更完整,安全性更高,秒级数据丢失(取决fsync策略,如果是everysec,最多丢失1秒的数据)
  • AOF文件是一个只进行追加的日志文件,且写入操作是以Redis协议的格式保存的,内容是可读的,适合误删紧急恢复

缺点:

  • 对于相同的数据集,AOF文件的体积要大于RDB文件,数据恢复也会比较慢
  • 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB。 不过在一般情况下, 每秒 fsync 的性能依然非常高

如何选择RDB和AOF

如果是数据不那么敏感,且可以从其他地方重新生成补回的,那么可以关闭持久化

如果是数据比较重要,不想再从其他地方获取,且可以承受数分钟的数据丢失,比如缓存等,那么可以只使用RDB

如果是用做内存数据库,要使用Redis的持久化,建议是RDB和AOF都开启,或者定期执行bgsave做快照备份,RDB方式更适合做数据的备份,AOF可以保证数据的不丢失

遗留的问题

Redis数据持久化只保证了数据能够从内存持久化到硬盘,但是,一旦很不幸你连硬盘也一起坏掉了呢,更极端一点,发生了一点天人祸,机房炸了。你的数据就没了。但是,Redis已经帮你考虑到了这一点了,那就是复制模式,复制模式就能够很好地解决异地容灾的问题,甚至还能够多个Redis实例还能分担读操作请求的压力。

详细的介绍可以参考这一篇文章[Redis主从复制]

参考自掘金用户TurboSnail的[Redis持久化机制:RDB和AOF]