前言
我们都知道redis的数据都是存储在内存中,而存储在内存中的一大弊端就是断电易失。
所以对redis中的数据进行持久化存储是非常必要的,本文主要介绍一个redis持久化的两种方式——RDB
和AOF
。
持久化的问题
在详细介绍RDB和AOF的理论之前,我们先来想象一个持久化的问题。
比如某个redis节点在8:00需要备份数据,假设每次备份需要30分钟,那么这个节点有两个状态可以选择——阻塞或者非阻塞。
在阻塞状态下,整个节点将在30分钟内不能对外提供服务,违背了高可用的原则,这无疑不是我们想要的状态。
在非阻塞状态下又有下面这个问题需要考虑:
当redis在8:00的时候开始向硬盘刷写数据,但是这半个小时之间如果redis中的数据发生了改变怎么办?
如果将改变后的数据继续向硬盘中刷写,那这数据算是8点的数据呢还是8点半的数据呢?
所以在非阻塞的情况下进行数据备份就会造成数据时点混乱的问题。
带着这个问题,我们再来介绍RDB和AOF。
RDB
这种持久化的方式又叫快照
(snapshotting)。
在默认情况下,redis会将数据库快照保存在名为dump.rdb的二进制文件中。
工作方式
当redis需要保存dump.rbd文件时,服务器会执行以下操作:
-
redis调用
forks()
,同时拥有父进程和子进程。 -
子进程将数据集写入一个临时的RDB文件中。
-
当子进程完成了对新RDB文件的写入后,redis会用新RDB文件代替原有的RDB文件,并将其删除。
工作原理
通过上面介绍的RDB的工作方式,会发现最核心的一步是redis调用forks。
那么我们就来说说这个forks。
不过在介绍forks之前,我们先来说说linux中的进程隔离
。
所谓进程隔离,就是指进程中的变量在其他进程中访问不到,除非本进程主动将该变量export
出去。
而forks
的作用是在当前进程的基础上创造出一个子进程,子进程中包含了父进程中的所有变量的副本。
由于进程之间的隔离性,所以父进程对变量的更改,子进程是感知不到的,反之亦然。
这种特性就把rdb备份的时点问题解决了。
在要备份的时间节点创建出一个子进程,这个子进程包含与当前进程完全一样的变量,但是对变量的改变又相互独立。
也就是说,子进程中的变量都是fork出来的时间节点的状态,父进程则可以继续对外提供服务,接收变量的改变操作。
需要注意一点的是,fork出来的子进程与父进程之间存在一个copy-on-write
的机制。
这个机制是说,当子进程刚刚被fork出来的时候,为了节省内存,与父进程是共享同一块内存区域的。
但是父进程中的某块数据发生改变后,就会另外开辟一块内存存放新数据,老数据接着被子进程持有。
优缺点
优点
RBD是一个非常紧凑的文件,保存了某个时间点的数据集,非常适用于数据集的备份,并且可以很方便的传送到远端的数据中心,很适合灾备。
RBD在保存的时候父进程唯一需要做的就是fork出一个子进程,剩下的工作都由子进程来做。这种方式可以最大化redis的性能。
与AOF相比,RDB在恢复大数据集的时候会更快一些。
缺点
redis要完整的保存整个数据集是个很繁重的工作,每次保存都要花费不短的时间。如果这段时间内发生了宕机,未被保存的数据就找不回来了。
当数据集很大的时候,fork也是很消耗时间的。
AOF
由于RDB的方式太“重”了,每次都需要把全量数据进行备份,这样每次备份的时间都会很长,万一在备份期间发生故障就会导致数据的永久丢失。
为了解决这个问题,1.1版本后提出了AOF的解决方案。
所谓AOF
,全称就是Append-only File。
可以通过在配置文件中打开AOF:
appendonly yes
当打开AOF之后,每当redis执行了一个改变了数据集的操作后,这个命令就会追加到AOF文件的末尾。
之后当redis重新启动之后,程序可以通过执行AOF文件中的命令来达到重建数据集的目的。
文件重写
我们都知道了AOF的工作方式是记录下来所有更改数据集的命令,所以随着redis的执行这个文件会变得越来越大。
但是很多情况下命令之间是可以进行合并的,比如对某个key自增了100次,在AOF文件中就会有100条对应的命令,这些命令是可以合并成1条的。
为了处理这种情况,redis增加了一个有趣的特性:在不打断服务端的情况下,对AOF文件进行重建——
执行BGREWRITEAOF
命令,redis将生成一个新的AOF文件,这个文件包含了重建当前数据集所需的最少命令。
重写原理
AOF重写与RDB创建快照一样,都巧妙地利用了写时复制机制:
-
redis执行fork命令,同时拥有父子进程。
-
子进程将新AOF文件写入临时文件。
-
对于新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有的AOF文件末尾。这样即使在重写的中途发生宕机,现有的AOF文件也是可靠的。
-
当子进程完成重写任务后,给父进程发一个信号,父进程将内存缓存中累积的所有命令追加到新AOF文件的末尾。
-
redis原子地用新文件代替老文件,之后所有的命令都追加到新AOF文件的末尾。
AOF的时效性
redis将AOF刷写回硬盘的频率是可以配置的,主要有下面三种模式:
-
每次有新命令添加进AOF文件时都回写进硬盘。
-
每秒刷写一次。
-
交给系统来决定什么时候刷写。
推荐(并且是默认)使用第二种,这种策略可以兼顾安全性和速度,要丢失也仅仅丢失一秒内的数据。
优缺点
优点
可靠性: AOF每秒将文件刷写回硬盘的配置会保证即使丢失也只丢失1秒内的数据,相比较RDB模式可靠性大幅度提高。
对内存友好:当AOF文件变得过大的时候在后台对文件进行重写,重写后的AOF文件包含了恢复当前数据集的最少命令集合,整个重写过程是绝对安全的。
可读性:AOF文件包含了所有写入操作,并且以redis协议的格式保存,因此AOF文件的内容很容易被人读懂,对文件进行分析也很轻松。
缺点
文件体积大:对相同的数据集来说,AOF文件的体积要比RDB文件要大。
数据集恢复速度不如RDB快。因为通过AOF恢复数据集要将所有的命令执行一遍。
选择哪种持久化方式?
一般来说,同时采用两种持久化方式可以达到最佳效果。
定时生成RDB快照非常便于进行数据库备份,RDB恢复数据集的速度比AOF快。
所以综合AOF与RDB的优点,应该整合两种持久化方式一同使用。
在redis4.0后,AOF文件中也包括了RDB全量。
总结
本文介绍了两种redis的持久化方式——RDB和AOF。
介绍了它们的工作原理以及优缺点,最后介绍了理想的持久化方案是怎样的——统合使用RDB与AOF。