redis-持久化机制RDB和AOF

1、RDB快照方式

在默认情况下,Redis将内存数据库快照保存在名字为dump.rdb的二进制文件中。

在指定的时间间隔内将内存中的数据集快照写入磁盘。

1.1 设置RDB持久化(默认开启)

你可以对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次数据集。 比如说, 以下设置会让 Redis 在满足“ 60 秒内有至少有 1000 个键被改动”这一条件时, 自动保存一次数据集:

save 60 1000 //关闭RDB只需要将所有的save保存策略注释掉即可

redis-持久化机制RDB和AOF_第1张图片

可以手动执行命令生成rdb快照,进入redis的客户端执行命令save或bgsave可以生成dump.rdb文件,每次命令执行都会将所有redis内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件。

1.2 bgsave写时复制机制

Redis 借助操作系统提供的写时复制技术(Copy-On-Write, COW),在生成快照的同时,依然可以正常处理写命令。简单来说,bgsave 子进程是由主线程 fork 生成的,可以共享主线程的所有内存数据。 bgsave 子进程运行后,开始读取主线程的内存数据,并把它们写入 RDB 文件。此时,如果主线程对这些数据也都是读操作,那么,主线程和 bgsave 子进程相互不影响。但是,如果主线程要修改一块数据,那 么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。

结构图:
redis-持久化机制RDB和AOF_第2张图片

配置自动生成rdb文件后台使用的是bgsave方式。

1.3 fork()方法

bgsave的原理是fork()+copyonwrite

1、fork()有什么用

​ fork()用于创建一个子进程,注意是子进程,不是子线程。fork()出来的进程共享其父类的内存数据。仅仅是共享fork()出子进程的那一刻的内存数据,后期主进程修改数据对子进程不可见,同理,子进程修改的数据对主进程也不可见。比如:A进程fork()了一个子进程B,那么A进程就称之为主进程,这时候主进程子进程所指向的内存空间是同一个,所以他们的数据一致。但是A修改了内存上的一条数据,这时候B是看不到的,A新增一条数据,删除一条数据,B都是看不到的。而且子进程B出问题了,对我主进程A完全没影响,我依然可以对外提供服务,但是主进程挂了,子进程也必须跟随一起挂。这一点有点像守护线程的概念。Redis正是巧妙的运用了fork()这个牛逼的api来完成RDB的持久化操作。

2、redis中的fork()

​ Redis运用fork()。bgsave执行时,Redis主进程会判断当前是否有fork()出来的子进程,若有则忽略,若没有则会fork()出一个子进程来执行rdb文件持久化的工作,子进程与Redis主进程共享同一份内存空间,所以子进程可以搞他的rdb文件持久化工作,主进程又能继续他的对外提供服务,二者互不影响。我们说了他们之后的修改内存数据对彼此不可见,但是明明指向的都是同一块内存空间。这是咋搞得?肯定不可能是fork()出来子进程后顺带复制了一份数据出来,如果是这样的话比如我有4g内存,那么其实最大有限空间是2g,我要给rdb留出一半空间来,扯淡一样!那他咋做的?采取了copyonwrite技术。

3、rdb的备份

(1)复制一份dump.rdb文件

cp dump.rdb d.rdb

(2)杀掉redis服务

# 查看redis进程号
ps -ef|grep redis

# 杀掉redis进程
kill -9 进程号

redis-持久化机制RDB和AOF_第3张图片

(3)删掉dump.rdb文件

rm -f dump.db

(4)先把备份的文件拷贝到工作目录下,再修改配置文件的名称为原来的名称

mv d.rdb dump.db

(5)再重新启动redis服务

src/redis-server redis.conf

(6)连接客户端

src/redis-cli

(7)查看数据是否恢复

> keys *

4、RDB优势

(1)适合大规模的数据恢复

(2)对数据完整性和一致性要求不高更适合使用

(3)节省磁盘空间(二进制文件本身就比较小

(4)恢复速度快

5、劣势

(1)Fork 的时候,内存中的数据被克隆了一份,大致 2 倍的膨胀性需要考虑。

(2)虽然 Redis 在 fork 时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。

(3)在备份周期在一定间隔时间做一次备份,所以如果 Redis 意外 down 掉的话,就会丢失最后一次快照后的所有修改

4、copyonwrite

5、save与bgsave对比:
redis-持久化机制RDB和AOF_第4张图片

2、AOF持久化 (append-only file)

2.1 、AOF默认不开启

可以在redis.conf中配置文件名称,默认为appendonly.aof

AOF文件的保存路径,同RDB的路径一致。

AOF和RDB同时开启,系统默认取AOF的数据

(1)向redis中设置值

[root@localhost redis-6.2.6]# src/redis-cli
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set k11 v11
OK
127.0.0.1:6379> set k12 v12
OK
127.0.0.1:6379> set k13 v13
OK
127.0.0.1:6379> set k14 v14
OK
127.0.0.1:6379> 

(2)再开启一个连接,查看aof文件的大小,发现appendonly.aof文件的大小由原来的0变为147

[root@localhost redis-6.2.6]# ll
total 344
-rw-rw-r--.  1 root root 33624 Oct  4  2021 00-RELEASENOTES
-rw-r--r--.  1 root root   147 Jun 17 16:46 appendonly.aof
-rw-rw-r--.  1 root root    51 Oct  4  2021 BUGS
-rw-rw-r--.  1 root root  5026 Oct  4  2021 CONDUCT
-rw-rw-r--.  1 root root  3384 Oct  4  2021 CONTRIBUTING
-rw-rw-r--.  1 root root  1487 Oct  4  2021 COPYING
drwxrwxr-x.  7 root root   213 Jun 15 23:51 deps
-rw-r--r--.  1 root root   124 Jun 17 16:46 dump.rdb

2.2 AOF数据备份

(1)下面演示,aof如何数据备份的,首先是复制一份aof文件

[root@localhost redis-6.2.6]# cp appendonly.aof appendonly.aof.bak

(2)关掉redis服务

127.0.0.1:6379> shutdown

(3)删掉appendonly.aof文件

[root@localhost redis-6.2.6]# rm -rf appendonly.aof

(4)开始数据恢复,将备份文件名称改为原来的名称

[root@localhost redis-6.2.6]# mv appendonly.aof.bak appendonly.aof

(5)再重启redis服务,查看数据已经恢复

[root@localhost redis-6.2.6]# src/redis-server redis.conf
[root@localhost redis-6.2.6]# ps -ef|grep redis
root      37949      1  0 16:59 ?        00:00:00 src/redis-server *:6379
root      38389   1454  0 16:59 pts/0    00:00:00 grep --color=auto redis
[root@localhost redis-6.2.6]# src/redis-cli
127.0.0.1:6379> keys *
1) "k13"
2) "k12"
3) "k14"
4) "k11"
127.0.0.1:6379> 

2.3 异常恢复

(1)修改默认的appendonly no,改为yes

(2)演示AOF文件损坏的情况,如何进行文件修复

在文件中加点东西。保存退出

[root@localhost redis-6.2.6]# vim appendonly.aof 

redis-持久化机制RDB和AOF_第5张图片

(3)重启redis,发现重启失败
redis-持久化机制RDB和AOF_第6张图片

(4)使用命令进行恢复

[root@localhost redis-6.2.6]# src/redis-check-aof --fix appendonly.aof 
0x              93: Expected prefix '*', got: 'h'
AOF analyzed: size=154, ok_up_to=147, ok_up_to_line=34, diff=7
This will shrink the AOF from 154 bytes, with 7 bytes, to 147 bytes
Continue? [y/N]: y
Successfully truncated AOF
[root@localhost redis-6.2.6]# 

(5)再重启redis,发现启动成功

[root@localhost redis-6.2.6]# src/redis-server redis.conf
[root@localhost redis-6.2.6]# ps -ef|grep redis
root       9142      1  0 17:36 ?        00:00:00 src/redis-server *:6379
root       9326   1458  0 17:36 pts/0    00:00:00 grep --color=auto redis

2.3 AOF同步频率设置

1 appendfsync always:始终同步,每次redis的写入都会立刻记入日志,性能较差但数据完整性比较好。
2 appendfsync everysec:每秒 fsync(同步) 一次,足够快,并且在故障时只会丢失 1 秒钟的数据。
3 appendfsync no:从不 fsync ,将数据交给操作系统来处理。更快,也更不安全的选择。

2.4、基本原理

就是每次都在aof文件后面追加命令,他与主进程收到请求和处理请求是串行化的,而非异步并行的。
在这里插入图片描述

RDB存在的缺点:RDB快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据

从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方 式: AOF 持久化,将修改的每一条指令记录以日志的文件保存到appendonly.aof中(先写入os cache,每隔一段时间 fsync到磁盘),只追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一遍以来恢复数据的工作。

可以通过修改redis.conf配置文件来打开AOF功能:改成yes

 # appendonly yes

redis-持久化机制RDB和AOF_第7张图片

从现在开始, 每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文件的末尾。

这样的话,当 Redis 重新启动时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。

你可以配置 Redis 多久才将数据 fsync (同步)到磁盘一次。

有三个选项:

1 appendfsync always:每次有新命令追加到 AOF 文件时就执行一次 fsync ,非常慢,也非常安全。
2 appendfsync everysec:每秒 fsync(同步) 一次,足够快,并且在故障时只会丢失 1 秒钟的数据。
3 appendfsync no:从不 fsync ,将数据交给操作系统来处理。更快,也更不安全的选择。

推荐(并且也是默认)的措施为每秒 fsync(同步) 一次, 这种 fsync 策略可以兼顾速度和安全性。

2.5 AOF重写

1、为什么要重写?

如果是一个简单的业务,运行很长时间,delete k1,set k1 XXX,反复的设置、删除,这样时间长了后数据会很大,但是这些都是无用的命令,导致aof文件最后会变的很大。如果某天redis宕机了,想要通过这个巨大的aof文件去恢复数据,效率会很低。所有就出现了AOF重写。

rewrite原理
redis-持久化机制RDB和AOF_第8张图片

1、aof_rewrite_buf:(重写)缓冲区,aof_buf:写命令存放的缓冲区。

2、开始bgrewriteaof的时候,判断当前有没有bgsave/bgrewriteaof在执行,若有,则不执行。

3、主进程fork()出子进程,在执行fork()这个方法的时候是阻塞的,子进程创建完毕后就不阻塞了。

4、主进程fork完子进程后,主进程能继续接收客户端的请求,所有写命令依然是写入AOF文件缓冲区并根据配置文件的策略同步到磁盘的。

5、因为fork的子进程仅仅共享主进程fork()时的内存,后期主进程在更改内存数据,子进程是不可见的。因此Redis采取重写缓冲区(aof_rewite_buf)保存fork之后的客户端请求。防止新AOF文件生成期间丢失主进程执行的新命令所生成的数据。所以此时客户端的写请求不仅仅写入原来的aof_buf缓冲区,还写入了重写缓冲区。这就是我为什么用深蓝色的框给他两框到一起的原因。

6、子进程通过内存快照的形式,开始生成新的aof文件。

7、新aof文件生成完后,子进程向主进程发信号。

8、主进程收到信号后,会把重写缓冲区(aof_rewrite_buf)中的数据写入到新的AOF文件(主要避免这部分数据丢失)

9、使用新的AOF文件覆盖旧的AOF文件,且标记AOF重写完成。

2.6、RDB和AOF比较

redis-持久化机制RDB和AOF_第9张图片

3、Redis4.0混合持久化

重启Redis时,如果使用RDB来恢复内存状态,可能会导致丢失大量数据,这个也是RDB的一大缺点。

使用AOF日志重放,但是重放AOF日志性能相对RDB来说要慢很多,如果AOF文件很大,那么重放的时间就需要花费很长时间。Redis4.0结合RDB和AOF各自的优点,带来了一个新的解决持久化方案 - 混合持久化。

通过下面的配置可以开启混合持久化(必须先开启AOF

# aof‐use‐rdb‐preamble yes

如果开启了混合持久化,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一 起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。

于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,因此重启效率大幅得到提升。

原理:

混合持久化也是通过bgrewriteaof完成的,所以基本流程和上述一样。不同的是当开启混合模式时,fork出的子进程先将共享的内存副本全量以RDB的方式写入aof。这样提高了速度也极大的缩小了aof文件(毕竟都是二进制)。写完还是通知主进程,然后再将重写缓冲区的内容以AOF方式写入到文件,然后替换旧的aof文件。也就是说这种模式下的aof文件发生rewrite后前半部分是rdb格式(REDIS开头的二进制数据),后半部分是正常的aof追加的命令(重写缓冲区里的)

混合持久化AOF文件结构如下:

redis-持久化机制RDB和AOF_第10张图片

1、混合持久化的优点:

混合持久化结合了RDB持久化和AOF持久化的优点,采取了rdb的文件小易于灾难恢复,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。

你可能感兴趣的:(Redis,redis,缓存,nosql)