目录
- 1 Redis持久化
- 1.1 持久化流程
- 1.2 RDB机制
- 1.2.1 save触发方式
- 1.2.2 bgsave触发方式
- 1.2.3 自动触发
- 1.2.4 RDB的优势和劣势
- 1.2.4.1 优势
- 1.2.4.2 劣势
- 1.3 AOF机制
- 1.3.1 写后日志优势和风险
- 1.3.2 AOF三种触发机制
- 1.3.3 文件重写
- 1.3.3.1 重写的作用
- 1.3.3.2 重写的过程
- 1.3.3.3 总结
- 1.3.4 优缺点
- 1.3.4.1 优点
- 1.3.4.2 缺点
- 1.4 两者混合
1 Redis持久化
Redis
数据是存储在内存中的,但是我们都知道内存的数据变化是很快的,也容易发生丢失,为了保证Redis
数据不丢失,那就要把数据从内存存储到磁盘上,以便在服务器重启后还能够从磁盘中恢复原有数据,这就是Redis
的数据持久化。Redis
数据持久化有三种方式:
-
AOF
日志(Append Only File
,文件追加方式):记录所有的操作命令,并以文本的形式追加到文件中。 -
RDB
快照(Redis DataBase
):将某一个时刻的内存数据,以二进制的方式写入磁盘(早期默认方式)。 - 混合持久化方式:
Redis 4.0
新增了混合持久化的方式,集成了RDB
和AOF
的优点
1.1 持久化流程
既然redis
的数据可以保存在磁盘上,那么这个流程是什么样的呢?
要有下面五个过程:
- 客户端向服务端发送写操作(数据在客户端的内存中)
- 数据库服务端接收到写请求的数据(数据在服务端的内存中)
- 服务端调用
write
这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中) - 操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)
- 磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)
1.2 RDB机制
RDB
其实就是把数据以快照的形式保存在磁盘上。什么是快照呢,你可以理解成把当前时刻的数据拍成一张照片保存下来。RDB
持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是 默认的持久化方式(早期)
,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb
。RDB
采用的是内存快照
的方式,它记录的是某一时刻的数据,而不是操作,所以采用RDB
方法做故障恢复时只需要直接把RDB
文件读入内存即可,实现快速恢复。
在我们安装了redis
之后,所有的配置都是在redis.conf
文件中,里面保存了RDB
和AOF
两种持久化机制的各种配置
既然RDB
机制是通过把某个时刻的所有数据生成一个快照来保存,那么就应该有一种触发机制,是实现这个过程。对于RDB
来说,提供了三种机制:save
、bgsave
、自动化
。我们分别来看一下
1.2.1 save触发方式
该命令会阻塞当前Redis
服务器,执行save
命令期间,Redis
不能处理其他命令,直到RDB
过程完成为止。具体流程如下:
执行完成时候如果存在老的RDB
文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取
1.2.2 bgsave触发方式
执行该命令时,Redis
会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体流程如下:
具体操作是Redis
进程执行fork
操作创建子进程,RDB
持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork
阶段,一般时间很短。基本上Redis
内部所有的RDB
操作都是采用bgsave
命令即默认配置
在执行快照的同时,Redis
会借助操作系统提供的写时复制技术(Copy-On-Write, COW
),正常处理写操作。bgsave
子进程是由主线程 fork
生成的,可以共享主线程的所有内存数据。bgsave
子进程运行后,开始读取主线程的内存数据,并把它们写入 RDB
文件
Redis
是怎么解决在bgsave
做快照的时候允许数据修改呢?
这里主要是利用bgsave
的子线程实现的,具体操作如下:
如果主线程执行读操作,则主线程和 bgsave
子进程互相不影响;
如果主线程执行写操作,则被修改的数据会复制一份副本,然后,主线程
在这个数据副本上进行修改。同时,bgsave
子进程可以继续把原来的数据 写入 RDB
文件,在这个过程中,主线程仍然可以直接修改原来的数据
虽然 bgsave
执行时不阻塞主线程,但是,如果频繁地执行全量快照,也会带来两方面的开销:
- 一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环(所以,在
Redis
中如果有一个bgsave
在运行,就不会再启动第二个bgsave
子进程) - 另一方面,
bgsave
子进程需要通过fork
操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork
这个创建过程本身会阻塞主线程 ,而且主线程的内存越大,阻塞时间越长
。
需要注意
:Redis
对 RDB
的执行频率非常重要,因为这会影响快照数据的完整性以及 Redis
的稳定性,所以在 Redis 4.0
后,增加了 AOF
和 RDB
混合的数据持久化机制: 把数据以 RDB
的方式写入文件,再将后续的操作命令以 AOF
的格式存入文件,既保证了 Redis
重启速度,又降低数据丢失风险,内存快照以一定的频率执行,在两次快照之间,使用 AOF
日志记录这期间的所有命令操作
1.2.3 自动触发
自动触发是由我们的配置文件来完成的。在redis.conf
配置文件中,里面有如下配置,我们可以去设置:
-
save
:这里是用来配置触发Redis
的RDB
持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如save m n
。表示m
秒内数据集存在n
次修改时,自动触发bgsave
。
默认如下配置:
表示900
秒内如果至少有1
个key
的值变化,则保存save 900 1
表示300
秒内如果至少有10
个key
的值变化,则保存save 300 10
表示60
秒内如果至少有10000
个key
的值变化,则保存save 60 10000
不需要持久化,那么你可以注释掉所有的save
行来停用保存功能。 -
stop-writes-on-bgsave-error
:默认值为yes
。当启用了RDB
且最后一次后台保存数据失败,Redis
是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster
)发生了。如果Redis
重启了,那么又可以重新开始接收数据了 -
rdbcompression
:默认值是yes
。对于存储到磁盘中的快照,可以设置是否进行压缩存储。 -
rdbchecksum
:默认值是yes
。在存储快照后,我们还可以让redis
使用CRC64
算法来进行数据校验,但是这样做会增加大约10%
的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。 -
dbfilename
:设置快照的文件名,默认是dump.rdb
-
dir
:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名
1.2.4 RDB的优势和劣势
1.2.4.1 优势
RDB
文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。
生成RDB
文件的时候,redis
主进程会fork()
一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO
操作。RDB
在恢复大数据集时的速度比 AOF
的恢复速度要快。
1.2.4.2 劣势
RDB
快照是一次全量备份
,存储的是内存数据的二进制序列化形式,存储上非常紧凑。当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。
需要注意,Redis
对 RDB
的执行频率非常重要,因为这会影响快照数据的完整性以及 Redis
的稳定性,所以在 Redis 4.0
后,增加了 AOF
和 RDB
混合的数据持久化机制: 把数据以 RDB
的方式写入文件,再将后续的操作命令以 AOF
的格式存入文件,既保证了 Redis
重启速度,又降低数据丢失风险
1.3 AOF机制
全量备份总是耗时的,有时候我们提供一种更加高效的方式AOF
,工作机制很简单,redis
会将每一个收到的写命令都通过write
函数追加到文件中。通俗的理解就是日志记录
AOF
采用的是写后日志
的方式,Redis
先执行命令把数据写入内存,然后再记录日志到文件中。AOF
日志记录的是操作命令
,不是实际的数据,如果采用AOF
方法做故障恢复时需要将全量日志都执行一遍
AOF
里记录的是 Redis
收到的每一条命令,这些命令是以文本形式保存的。
我们以 Redis
收到set testkey testvalue
命令后记录的日志为例,看看 AOF
日志的内容。其中,*3
表示当前命令有三个部分,每部分都是由 $+数字
开头,后面紧跟着具体的命令、键或值
。这里,数字
表示这部分中的命令、键或值一共有多少字节。例如,$3 set
表示这部分有 3 个字节,也就是set
命令。
1.3.1 写后日志优势和风险
AOF
采用的是写后日志
的方式,我们平时用的MySQL
则采用的是 写前日志
,那 Redis
为什么要先执行命令,再把数据写入日志呢?
这个主要是由于Redis
在写入日志之前,不对命令进行语法检查,所以只记录执行成功的命令,避免出现记录错误命令的情况,而且在命令执行后再写日志不会阻塞当前的写操作
后写日志的风险:
- 数据可能会丢失:如果
Redis
刚执行完命令,此时发生故障宕机,会导致这条命令存在丢失的风险
- 如果此时
Redis
是用作缓存,还可以从后端数据库重新读入数据进行恢复。 - 如果
Redis
是直接用作数据库的话,此时,因为命令没有记入日志,所以就无法用日志进行恢复了。
- 可能阻塞其他操作:
AOF
日志其实也是在主线程中执行,所以当Redis
把日志文件写入磁盘的时候,还是会阻塞后续的操作无法执行,(即:AOF
虽然避免了对当前命令的阻塞,但可能会给下一个操作带来阻塞风险)AOF
日志也是在主线程中执行(写回策略为always
时),如果在把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而导致后续的操作也无法执行了。
1.3.2 AOF三种触发机制
- 每修改同步
always
:每个写命令执行完,立马同步地将日志写回磁盘,同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好 - 每秒同步
everysec
:每个写命令执行完,只是先把日志写到AOF
文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘
异步操作,每秒记录,如果一秒内宕机,有数据丢失, - 不同步
no
:每个写命令执行完,只是先把日志写到AOF
文件的内存缓冲区
,由操作系统决定何时将缓冲区内容写回磁盘
配置项 | 写回时机 | 优点 | 缺点 |
Always | 同步写回 | 可靠性高,数据基本不丢失 | 每个写命令都有落盘,性能影响较大 |
Everysec | 每秒写回 | 性能适中 | 宕机时丢失1秒内的数据 |
No | 操作系统控制的写回 | 性能好 | 宕机时丢失数据较多 |
我们就可以根据系统对高性能和高可靠性的要求,来选择使用哪种写回策略了。
- 想要获得高性能,就选择
No
策略; - 想要得到高可靠性保证,就选择
Always
策略; - 允许数据有一点丢失,又希望性能别受太大影响的话,那么就选择
Everysec
策略
1.3.3 文件重写
1.3.3.1 重写的作用
AOF
是以文件的形式在记录接收到的所有写命令。随着接收的写命令越来越多,AOF 文件会越来越大
。这也就意味着,我们一定要小心 AOF
文件过大带来的性能问题,主要在于以下三个方面:
- 文件系统本身对文件大小有限制,无法保存过大的文件;
- 如果文件太大,之后再往里面追加命令记录的话,效率也会变低;
- 如果发生宕机,
AOF
中记录的命令要一个个被重新执行,用于故障恢复,如果日志文件太大,整个恢复过程就会非常缓慢,这就会影响到Redis
的正常使用。
AOF 重写机制
就是在重写时,Redis
根据数据库的现状创建一个新的 AOF
文件,也就是说,读取数据库中的所有键值对,然后对每一个键值对用一条命令记录它的写入
。重写机制具有多变一
功能。所谓的多变一
,也就是说,旧日志文件中的多条命令,在重写后的新日志中变成了一条命令。
1.3.3.2 重写的过程
AOF
日志由主线程写回不同,重写过程是由 后台子进程 bgrewriteaof 来完成的,这也是为了避免阻塞主线程
,导致数据库性能下降。
可以把重写的过程总结为 一个拷贝,两处日志
:
-
一个拷贝
:就是指,每次执行重写时,主线程fork
出后台的bgrewriteaof
子进程。此时,fork
会把主线程的内存拷贝一份给bgrewriteaof
子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof
子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。 -
第一处日志
:指的是因为主线程未阻塞,仍然可以处理新来的操作,Redis
会把这个操作写到它的缓冲区。这样一来,即使宕机了,这个AOF
日志的操作仍然是齐全的,可以用于恢复。 -
第二处日志
:就是指新的AOF
重写日志。这个操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作。等到拷贝数据的所有操作记录重写完成后,重写日志记录的这些最新操作也会写入新的AOF
文件,以保证数据库最新状态的记录。
此时,我们就可以用新的 AOF
文件替代旧文件了。
为了压缩aof
的持久化文件。redis
提供了bgrewriteaof
命令。将内存中的数据以命令的方式保存到临时文件中,同时会fork
出一条新进程来将文件重写。
1.3.3.3 总结
总结来说,每次 AOF
重写时,Redis
会先执行一个内存拷贝,用于重写;然后,使用两个日志保证在重写过程中,新写入的数据不会丢失。而且,因为 Redis
采用子进程进行日志重写,所以,这个过程并不会阻塞主线程 。
正因为记录的是操作命令,而不是实际的数据,所以,用 AOF
方法进行故障恢复的时候,需要逐一把操作日志都执行一遍。如果操作日志非常多,Redis
就会恢复得很缓慢,影响到正常使用
1.3.4 优缺点
1.3.4.1 优点
-
AOF
可以更好的保护数据不丢失,一般AOF
会每隔1
秒,通过一个后台线程执行一次fsync
操作,最多丢失1
秒钟的数据。 -
AOF
日志文件没有任何磁盘寻址
的开销,写入性能非常高,文件不容易破损。 -
AOF
日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。 -
AOF
日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall
命令清空了所有数据,只要这个时候后台rewrite
还没有发生,那么就可以立即拷贝AOF
文件,将最后一条flushall
命令给删了,然后再将该AOF
文件放回去,就可以通过恢复机制,自动恢复所有数据
1.3.4.2 缺点
- 对于同一份数据来说,
AOF
日志文件通常比RDB
数据快照文件更大 -
AOF
开启后,支持的写QPS
会比RDB
支持的写QPS
低,因为AOF
一般会配置成每秒fsync
一次日志文件,当然,每秒一次fsync
,性能也还是很高的 - 以前AOF发生过bug,就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来
1.4 两者混合
虽然 bgsave
执行时不阻塞主线程,但是,如果频繁地执行全量快照,也会带来两方面的开销。
- 一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环(所以,在
Redis
中如果有一个bgsave
在运行,就不会再启动第二个bgsave
子进程) - 另一方面,
bgsave
子进程需要通过fork
操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork
这个创建过程本身会阻塞主线程,而且主线程的内存越大,阻塞时间越长
。
RDB
和AOF
到底该如何选择
选择的话,两者加一起才更好。因为两个持久化机制你明白了,剩下的就是看自己的需求了,需求不同选择的也不一定,但是通常都是结合使用。有一张图可供总结:
需要注意
:Redis
对 RDB
的执行频率非常重要,因为这会影响快照数据的完整性以及 Redis
的稳定性,所以在 Redis 4.0
后,增加了 AOF
和 RDB
混合的数据持久化机制,简单来说:内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作
,这样一来,快照不用很频繁地执行,这就避免了频繁 fork
对主线程的影响。而且,AOF
日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销