1 Redis持久化
Redis
数据是存储在内存中的,但是我们都知道内存的数据变化是很快的,也容易发生丢失,为了保证Redis
数据不丢失,那就要把数据从内存存储到磁盘上,以便在服务器重启后还能够从磁盘中恢复原有数据,这就是Redis
的数据持久化。
Redis
数据持久化有三种方式:
-
AOF
日志(Append Only File
,文件追加方式):记录所有的操作命令,并以文本的形式追加到文件中。 -
RDB
快照(Redis DataBase
):将某一个时刻的内存数据,以二进制的方式写入磁盘(早期默认方式)。 - 混合持久化方式:
Redis 4.0
新增了混合持久化的方式,集成了RDB
和AOF
的优点
1.1 持久化
redis集群同步:
1.1.1 持久化流程
既然redis
的数据可以保存在磁盘上,那么这个流程是什么样的呢?
要有下面五个过程:
- 客户端向服务端发送写操作(数据在客户端的内存中)
- 数据库服务端接收到写请求的数据(数据在服务端的内存中)
- 服务端调用
write
这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中) - 操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)
- 磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)
1.1.2 数据同步机制
对于多副本模式,Redis
和关系型数据库一样,提供了主从库模式来保证数据副本的一致性。主从库之间采用的是读写分离的方式,即读操作
可以被主库/从库
接收,但是写操作只能先被主库接收执行然后才由主库同步给从库。
那么问题来了,为啥要用读写分离实现?
如果每次的修改请求都发到不同的实例上,要保证数据的一致性,就需要涉及到
加锁和协商
是否完成修改等操作,这会带来很大的性能开销。而如果所有的修改都只在主库上进行,就无需协调三个实例,只需要同步给从库,进而保持数据一致性。
1.1.3 Redis主从库同步流程详解
假设现在我们有两个实例,分别是主库实例1(172.16.19.3)和实例2(172.16.19.5),我们的目标就是让实例2成为实例1的从库,并进行数据同步
1.1.3.1 第一次同步过程
在实战中,只需要实例2的shell中执行以下命令就可以将实例2作为实例1的从库,并从实例1上复制数据
replicaof 172.16.19.3 6369
虽然就这一个命令真简单,而在这背后,Redis偷偷摸摸地进行了三个阶段的操作,如下图所示
-
阶段1
:从库向主库发送psync命令,表示进行数据同步。该命令格式如下所示:
命令格式:psync runID offset# psync ? -1
其中?
代表从库并不知道主库的runID,-1
表示第一次复制
主库收到psync
命令确认后,会向从库发送一个FULLERSYNC
的响应命令,并带上主库的runID
和目前的复制进度offset
。
命令格式:FULLERSYNC runID offset
FULLERSYNC
代表全量复制,一般用于第一次数据同步 -
阶段2
:主库通过发送RDB
文件给从库,从库收到后在本地完成数据加载。
需要注意的点:- 主库会首先执行一次
bgsave
(bgsave
不会阻塞主线程)生成RDB
文件,然后才发送给从库。 - 从库会首先清空已有的数据,然后再加载
RDB
数据,因为在同步之前可能保存了其他数据,不清除的话可能会数据不一致。 - 主库在同步数据给从库中产生的写操作会用专门的
replication buffer
记录,然后在第三个阶段同步过去。
- 主库会首先执行一次
-
阶段3
:主库将第二阶段执行过程收到的新的写操作命令,同步给从库。
在实现中,主库会将新收到的写操作放到replication buffer
中记录下来,然后将这些操作修改发给从库,从库收到后再重新执行一遍这些操作。
以上三个阶段完成之后,主从库就算完成了一次数据同步。
1.1.3.2 为什么用RDB不用AOF
Redis
主库在向从库同步数据时使用的RDB
文件,那么AOF
记录的操作命令更全,相比RDB
丢失的数据更少,为什么主库用RDB
不用AOF
呢?
-
RDB
读取速度相对较快,从库可以快速完成RDB
的读取,然后再去消费replication buffer
的数据完成一次同步。而如果使用AOF
,其体积大读取速度慢,且需要更大空间的replication buffer
,对于一个主节点多个从节点来说的话,内存的占用就会更大; -
AOF
是Append
追加模式,同时读写需要考虑并发安全问题,并且AOF
是文本文件,体积较大,浪费网络带宽
1.1.3.3 主从级联模式降低主库压力
主从库同步过程中,主库需要完成两个耗时的操作:生成RDB
文件 和 传输RDB
文件。现实场景中,从库一般都会有多个,如果都要和主库同步的话,会造成主库的性能压力 和 网络压力。
主库需要
fork
子进程来生成RDB
文件,这个fork
操作是会阻塞主线程处理正常请求的,虽然后续的bgsave
过程不会阻塞主线程。
此外,传输RDB
文件也会占用主库服务器的网络带宽。
Redis提供了主从级联
模式,也就是所谓的主-从-从
模式。
在主-从-从
模式下,新增的从库可以设置从 集群中的某一个从库 中进行数据同步
,从而避免每次都从主库进行同步,降低主库的资源消耗,保证系统的稳定性。
比如,在实际中通常会手动选择一个 内存配置较高的 从库 来作为同步源,其他新的从库加入后可以从这个从库中同步,建立主从关系。例如,下面的命令就可以实现新增从库和某一个从库建立主从关系:
replicaof 所选从库ID 6379
具体的示意图如下图所示:
1.1.3.4 全量同步后的增量同步
主从库通过FULLERSYNC
进行全量复制同步 + 主从级联模式分担主库压力 之后,Redis
的主从库之间就会一直维护一个长连接来进行增量的命令操作同步,这个过程又被称之为基于长连接的命令传播
。
为何选择长连接?因为可以避免频繁建立连接的开销。
虽然长连接很方便,但也存在一个风险点:主从库网络断了或 阻塞了。这个风险可能导致的问题就是:主从库之间数据无法保持一致,客户端可能从读库读到过时的数据。
当然,Redis
早就已经为我们想好了解决方案,不过得分为两个版本来看:
-
Redis 2.8
之前,如果出现了网络闪断,Redis
主从库间会重新进行一次全量复制。当然,全量复制就意味着有很大的开销。 -
Redis 2.8
之后,如果出现了网络闪断,Redis
主从库间会采用增量复制的方式继续同步。可以看到,增量复制肯定比全量复制开销要小得多。主要用于网络中断等情况后的复制,只将中断期间mater
执行的写命令发送给slave
,与全量复制相比更加高效。
这里的增量复制的核心要点就在于Redis
引入了repl_backlog_buffer
缓冲区,现在我们就来看看这个repl_backlog_buffer
到底是个啥。
repl_backlog_buffer
是一个环形缓冲区,主库会记录自己写到的位置,从库则会记录自己已经读到的位置。下图展示了repl_backlog_buffer
的示意图:
可以看到,正常情况下,主从库的偏移量基本相等。随着主库不断接收新的写操作,它在缓冲区中的写位置会逐步偏离起始位置。对主库来说,对应的偏移量就是 master_repl_offset
,只要主库的新写操作越多,这个值也就越大。同理,从库在复制完写操作命令后,它在缓冲区中的读位置也开始逐步偏移起始位置。对从库来说,其对应的已复制偏移量就是 slave_repl_offset
。
当网络闪断异常情况下,主库可能会接收到新的写操作命令,因此,可以看出,主库的偏移量master_repl_offset > 从库的偏移量slave_repl_offset
。那么,此时就只需要把 master_repl_offset 和 slave_repl_offset 之间的命令操作同步给从库就ok了。
综上所述,增量同步的过程整体如下:
在
Redis 7.0
之后,采用了共享缓冲区
的设计。因为不管是全量复制
还是增量复制
,当写请求到达 master
时,指令会分别写入所有 slave
的 replication buffer
以及 repl_backlog_buffer
。重复保存,太浪费内存了。共享缓冲区
:既然存储内容是一样,直接的做法就是主从复制在命令传播时,将这些写命令放在一个全局的复制缓冲区中,多个 slave
共享这份数据,不同 slave
引用缓冲区的不同内容。
1.1.3.5 repl_backlog_buffer扩展
在增量复制过程中,需要注意的点:repl_backlog_buffer
是一个环形缓冲区,在缓冲区被写满了之后,主库再次写入时就会覆盖掉之前写入的操作。
那么问题来了,如果从库读取的速度很慢,就有可能出现从库读取到了不一致的数据。如何解决?
Redis
提供了一个 repl_backlog_size
的参数,它与缓冲区的空间大小紧密相关。在实际中,一般其设置为:repl_backlog_size = 缓冲区空间大小 * 2
,这样可以降低由于读库消费速度慢导致的数据不一致。
缓冲空间的计算公式是:
缓冲空间大小 = 主库写入命令速度 * 操作大小 - 主从库间网络传输命令速度 * 操作大小
但是,如果并发请求量特别大,两倍的缓冲区空间都不够用,主从库仍然存在不一致的风险,那么此时,可能需要根据Redis
所在服务器的性能指标(主要是内存资源)再增加一些 repl_backlog_size
值。
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
1.2.2.1 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.2.2 写时复制
点击此次了解Linux中写时复制技术
执行 bgsave
命令的时候,会通过 fork()
创建子进程,此时子进程和父进程是共享同一片内存数据的,因为创建子进程的时候,会复制父进程的页表,但是页表指向的物理内存还是一个,此时如果主线程执行读操作,则主线程和 bgsave
子进程互相不影响。
如果主线程执行写操作,则被修改的数据会复制一份副本,然后 bgsave
子进程会把该副本数据写入 RDB
文件,在这个过程中,主线程仍然可以直接修改原来的数据。
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
出一条新进程来将文件重写。
设置 AOF
重写策略,例如配置auto-aof-rewrite-percentage
和auto-aof-rewrite-min-size
,让Redis
自动在文件增长到一定比例
或超过最小尺寸
时触发AOF
重写。
1.3.3.3 总结
总结来说,每次 AOF
重写时,Redis
会先执行一个内存拷贝,用于重写;然后,使用两个日志保证在重写过程中,新写入的数据不会丢失。而且,因为 Redis
采用子进程进行日志重写,所以,这个过程并不会阻塞主线程 。
正因为记录的是操作命令,而不是实际的数据,所以,用 AOF
方法进行故障恢复的时候,需要逐一把操作日志都执行一遍。如果操作日志非常多,Redis
就会恢复得很缓慢,影响到正常使用
1.3.4 AOF增量同步
AOF
增量同步:配置aof-rewrite-incremental-fsync
选项,可以控制在AOF
重写过程中增量地进行fsync
操作,从而减少AOF
文件过大时的同步操作开销。
这个选项的作用是控制Redis
在执行AOF
重写时对磁盘的同步频率。默认情况下,当进行AOF
重写时,Redis
会在重写完成后执行一次完整的fsync
操作来确保AOF
文件的持久性,这可能会导致性能下降,特别是对于大型AOF
文件而言。启用 aof-rewrite-incremental-fsync
后,Redis
会在重写AOF
文件的过程中增量地进行fsync
操作,而不是在重写完成后进行一次完整的fsync
操作,这样可以降低磁盘I/O压力,提高性能。
要启用 aof-rewrite-incremental-fsync
,你需要在Redis配置文件中设置该选项为 yes:
aof-rewrite-incremental-fsync yes
启用此选项后,Redis
在进行AOF
重写时将采用增量fsync
操作,以降低同步操作的开销。
这样,当AOF
文件过大时,Redis
就可以更加高效地进行AOF
重写,减少同步操作对性能的影响
1.3.5 优缺点
1.3.5.1 优点
-
AOF
可以更好的保护数据不丢失,一般AOF
会每隔1
秒,通过一个后台线程执行一次fsync
操作,最多丢失1
秒钟的数据。 -
AOF
日志文件没有任何磁盘寻址
的开销,写入性能非常高,文件不容易破损。 -
AOF
日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。 -
AOF
日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall
命令清空了所有数据,只要这个时候后台rewrite
还没有发生,那么就可以立即拷贝AOF
文件,将最后一条flushall
命令给删了,然后再将该AOF
文件放回去,就可以通过恢复机制,自动恢复所有数据
1.3.5.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
日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销