这是本人学习的总结,主要学习资料如下
- B站狂神说,redis教程
- 马士兵
目录
1、RDB持久化和AOF持久化概述
这两个是redis的持久化策略。
所谓的持久化,就是redis为了防止内存中的数据丢失,于是将当前内存的数据用各种形式存储到内存中,这样就能从意外停机中恢复数据。
RDB是Redis Data Base
的缩写,AOF是Append Only File
的缩写。
RDB模式保存的文件默认是dump.rdb
,而AOF则是dump.aof
2、RDB持久化
2.1、RDB持久化策略
RDB的持久化策略如下图所示。
主进程用来处理redis的指令,同时会有一个子进程专门用来处理持久化操作。
redis数据发生改变时,主进程将数据写入常规的内存数据库时,也会用os写时复制机制将改变的情况写入共享内存中。
子进程到共享内存中读取变化的数据,然后写入临时文件。临时文件过一段时间后会将变更信息写入正式文件。
整个过程主进程不会产生IO操作,这样即使持久化时发生错误,也能保证服务正常运行。
2.2、RDB特性
- 不影响redis服务:因为会有子进程专门负责持久化工作,即使持久化工作出错也不会影响主进程。需要注意,子进程的工作确实不会阻塞主进程,但是主进程需要
fork
出子进程去完成持久化操作。这个fork
是会阻塞主进程的。所以我们也不能频繁地去进行RDB持久化。 - 较高的性能:如上所述,主进程可以安心处理自己的工作,所以服务会更稳定,性能较高。
- 不保证完全持久化:因为持久化错误后不影响redis的服务,所以redis的变更可能不是很完整。
2.3、启动RDB持久化的方式
2.3.1、手动执行命令
bgsave
:创建子线程去执行RDB快照,主线程不受影响。
可以看到bgsave
执行后显示后台开始持久化=.
127.0.0.1:6379> bgsave
Background saving started
save
: 立即开始RDB持久化。
127.0.0.1:6379>save
ok
2.3.2、通过配置定时自动持久化
有关RDB持久化的配置项是save withinSeconds num
。其中save
是关键字,withinSeconds
和num
都是整数。
withinSeconds
表示时间,单位是秒。num
表示写操作的次数。这个命令的含义就是如果在withinSeconds
秒内发生num
次写操作,那就执行一次RDB持久化。
比如save 300 10
表示,如果在300s
内发生10
次写操作,那就执行一次RDB持久化。
那么常规来说,修改配置就两种方式,一个是通过config set key value
修改配置后,使用save
保存即可。
127.0.0.1:6379> config set save "10 10"
OK
127.0.0.1:6379> save
OK
127.0.0.1:6379> config get save
1) "save"
2) "10 10"
另一种是在redis.conf
中修改,重启redis即生效。以下是默认配置
# save 900 1
# save 300 10
# save 60 10000
需要注意的是,在配置文件中可以设置多个条件的save
。设置多个的话,只要满足其中任意一条,那redis就会启动一次RDB持久化
2.3.3、关闭服务时自动保存
当我们使用shutdown
关闭redis时,redis也会触发一次RDB持久化。
下面是我执行shutdown
后redis的日志,我们可以看到它在22:15:44
时完成DB saved on disk
。
56416:M 01 Feb 2023 21:29:22.914 * Background saving terminated with success
56416:M 01 Feb 2023 22:02:24.966 * DB saved on disk
56416:M 01 Feb 2023 22:15:44.830 # User requested shutdown...
56416:M 01 Feb 2023 22:15:44.830 * Saving the final RDB snapshot before exiting.
56416:M 01 Feb 2023 22:15:44.831 * DB saved on disk
56416:M 01 Feb 2023 22:15:44.831 # Redis is now ready to exit, bye bye...
我们查看对应的dump.rdb
文件,修改时间也是22:15:44
。
2.4、RDB数据丢失问题
这很容易想到,因为RDB的持久化不是每次操作都会去持久化,所以如果在下一次持久化前发生宕机,那这中间的数据就会丢失。
下图就是T1
时刻发生写入操作,但持久化没开始前就发生宕机,于是修复后的数据缺失了T1
时刻的内容。
3、AOF持久化
3.1、AOF持久化策略
AOF也会生成一个名为appendonly.aof
的文件存储数据。但是与RDB方式不同的是,这份文件会顺序存储写入的指令。当需要恢复数据时,就可以顺序执行这些指令达到恢复数据的目的。
AOF持久化逻辑是这样的:当redis接收到一条写命令时,会将该指令存放在一个缓存内存中,我们称为AOF缓冲
,如下图所示。等满足一些条件时(这些条件和配置appendfsync
有关,具体可以继续往下看),这些写入指令会写入appendonly.aof
文件的末尾。
3.1.1、指令写入文件的时机
我们可以选择想要的方案,选择缓存中的指令存进appendonly.aof
文件末尾的时间点。只需要设置配置文件中appendfsync
属性即可。
appendfsync always
:只要接收到一条写入指令,就立刻存进appendonly.aof
文件中。很明显,这样确实安全性很高,完全可以保证数据不丢失,但是频繁的IO操作会大大降低redis的性能,减少磁盘的寿命。appendfsync no
:不同步数据,相当于关闭了AOF。appendfsync everysec
:这算是第一种和第二种策略的折中,对写入的时机有控制,但不是控制很频繁。该策略是每过一秒就将指令写入appendonly.aof
文件中。
3.2、AOF重写
经过一定时间的运行后,appendonly.aof
文件肯定会越来越大。同时文件中有些命令是无效的,比如set k1 v1
,set k1 v2
, set k1 v3
,我们只需要存最后一条语句就行了。为了缩减appendonly.aof
文件的大小,redis就会执行AOF重写策略。
3.2.1、重写策略
- 父进程创建一个子进程,子进程负责将当前内存中的redis数据转化成写指令并将这些指令存进临时的
appendonly.aof
文件中。注意,该文件和旧的appendonly.aof
不是同一个,目前两者是同时存在的。 - 在子进程完成工作之前,若redis又接收到了新的写指令,父进程会将这些指令存进临时的指令缓存空间,同时也将这些指令写入旧的
appendonly.aof
文件中。这样即使在此期间系统发生故障,redis也能保证这段时间改变的数据及时存到旧的appendonly.aof
文件中。 - 等子进程完成工作后,会给父进程发送一个信号。父进程收到信号后会将第二步中提到的临时指令缓存空间中的指令追加到子进程创建的
appendonly.aof
文件中。 - 之后子进程创建的这个新
appendonly.aof
文件会替代旧的appendonly.aof
文件,完成重写。
3.2.2、AOF重写的时间
3.2.2.1、指令
通过指令主动重写:bgrewriteaof
。
127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
3.2.2.2、配置
有两个配置可以决定AOF重写的时机,这两个配置是一起使用的。
- 动态大小重写:
auto-aof-rewrite-percentage percentage
,从上次AOF结束开始计算,这段时间内如果AOF新增加的大小达到了上次AOF时appendonly.aof
的某个百分比,那就再执行一次AOF。
auto-aof-rewrite-percentage
是关键字。
percentage
是整数,表示百分比。
比如下面的命令是当新增加的内容大小超过上次AOFappendonly.aof
大小的50%则进行一次AOF。比如上次AOF时appendonly.aof
是50MB,当AOF缓冲区的大小达到25MB时就进行一次AOF。
auto-aof-rewrite-percentage 50
- 最低重写大小:
auto-aof-rewrite-min-size size
,对上一个配置的限制。上一个配置根据appenonly.aof
的大小百分比来决定是否AOF。这个配置的意思是除了AOF缓冲区达到上次AOF时appenonly.aof
的某个百分比以外,appenonly.aof
的大小还得超过size
才进行重写。
auto-aof-rewrite-min-size
是关键字。
size
是整数,需要自己写单位,比如64mb
。表示AOF缓冲区要超过size
后才能进行AOF重写。
比如下面的命令是当AOF缓冲区超过上次AOFappendonly.aof
大小的50%,并且AOF缓冲区超过64mb时,则进行一次AOF。比如上次AOF时appendonly.aof
是50MB,当AOF缓冲区的大小超过25MB并且超过最低限制64MB时进行AOF,所以AOF缓冲区到达64MB时会进行一次AOF重写。
auto-aof-rewrite-percentage 50
auto-aof-rewrite-min-size 64mb
3.3、开启和关闭AOF持久化
相关的配置是appendonly
。如果开启则是appendonly yes
,关闭则是appendonly no
。
3.4、修复被损坏备份文件
修复错误:redis-check-aof --fix
,自动修复。
3.5、Redis的重启恢复数据流程
流程图如下所示,简单一句话,有AOF就用AOF恢复数据,没有AOF才用RDB。
4、RDB和AOF混合保存数据
使用RDB时,数据有丢失的风险,因为两次RDB的间隔时间可能会比较长;
使用AOF的缺点是,redis经过一段时间后,appendonly.aof
会变得比较大。恢复数据时一条条执行指令可能会花较长的时间;
而RDB是存储快照,是二进制文件,空间小。且回复数据时基本上直接复制这个文件内容到内存中即可,速度远远快于AOF。
为了取长补短就有了两者的混合模式。
4.1、配置
aof-use-rob-preamble yes
aof-use-rob-preamble
是关键字。
yes
,开启混合模式则是yes
,不开启是no
。
4.2、流程
在混合模式下,redis正常还是用AOF的方式记录数据。和AOF不同的是,混合模式保存的appendonly.aof
的格式。
混合模式中appendonly.aof
被分为两个部分,前面是RDB的快照,后面是AOF的指令数据。
需要恢复数据时redis就可以通过保存的redis快照快速回复数据。
当然AOF持久化会每秒进行一次,这里持久化的内容只有指令,没有RDB的内容,和一般的AOF一样。
RDB的数据备份只有AOF重写发生时才会有一次。
流程如下。
5、常见问题
-
主线程,子线程和后台线程的区别
redis中,主线程主要就是负责和外界的读写,剩下的一小部分工作则是交给子线程和后台线程去完成。当然生成子线程或者调用后台线程也会调用主线程的一小部分时间。子线程由主线程
fork
生成,这个操作会让主线程短暂阻塞。子线程占用的空间都是属于主线程的,两者之间的数据是共享的,一般用来完成RDB和AOF的相关操作。
之所以用子线程去做持久化而不是用后台线程,是因为持久化过程中需要访问主线程存的数据,将这些数据从内存复制到硬盘中。主线程不和后台线程共享数据所以持久化不能用后台线程完成。后台线程是用来服务主线程的,他不属于主线程,他不占用主线程的空间。一般后台线程用来完成一些异步的操作,比如redis关闭后的一些后续操作。
- Redis持久化过程中有没有其它的阻塞风险?
当然有。前面提到过,尽管持久化工作都是由子线程完成,但是fork
出子线程是会令主线程短暂阻塞的。所以频繁持久化会导致主线程阻塞。
-
为什么主从复制不用AOF文件复制
appendonly.aof
是文本文件,是一堆字符串。使用这个文件复制数据需要一条条执行里面的指令,执行还涉及到语义分析,编译等过程,时间开销大。RDB文件是二进制文件,占用空间小,几乎可以看成内存中的内容直接复制到硬盘中。用这个文件复制数据只需要将该文件复制到内存中即可,时间快很多。