加入收藏 | 设为首页 | 会员中心 | 我要投稿 应用网_丽江站长网 (http://www.0888zz.com/)- 科技、建站、数据工具、云上网络、机器学习!
当前位置: 首页 > 站长学院 > MySql教程 > 正文

图解Redis持久化底层实现原理——RDB/AOF

发布时间:2023-10-26 12:07:00 所属栏目:MySql教程 来源:转载
导读: 背景
Redis作为一个内存数据库,数据是以内存为载体存储的,即所有数据都保存在内存中。一旦Redis服务器进程退出或宕机,即便重启redis服务mysql持久化,数据也会全部丢失。为了解决这个问

背景

Redis作为一个内存数据库,数据是以内存为载体存储的,即所有数据都保存在内存中。一旦Redis服务器进程退出或宕机,即便重启redis服务mysql持久化,数据也会全部丢失。为了解决这个问题,Redis提供了持久化机制,说白了就是把数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据!

★ Redis提供2种持久化方案:

一个是快照的方式,一个是类似日志追加的方式

什么是RDB?

RDB 就是 Redis DataBase 的缩写,中文名为快照/内存快照。RDB是一种快照存储持久化方式,原理就是将Redis某一时刻的内存数据写入到RDB文件中,并保存到磁盘上。

该文件是一个压缩过的二进制文件,默认文件名为dump.rdb,通过该文件可以还原redis数据库的数据。在Redis服务器启动时,会重新加载dump.rdb文件的数据到内存当中,达到恢复数据目的。

★ 可根据redis.conf 配置文件中设置rdb文件名 和 保存目录:

# 指定本地数据库文件名(rdb文件的名称),默认值为dump.rdb
dbfilename dump.rdb
# 指定本地数据库存放目录(dump.rdb文件存放目录),rdb、aof文件也会写在这个目录
dir /usr/local/var/db/redis/

——既然RDB持久化的方式是生成RDB文件,那么RDB文件是怎么生成的呢?

触发RDB文件生成的方式:

★手动触发

执行save和bgsave命令,可以手动触发快照,生成RDB文件

1、save

当某个客户端发送 save 命令后,此时会阻塞当前Redis服务器,在RDB文件创建完成之前是不能处理其他客户端发送的任何命令请求,如果数据量太大会造成长时间阻塞,期间redis无法处理其他请求,线上环境不建议使用。如下图所示:

mysql持久化_杜蕾斯持久装和杰士邦持久装_姜承尧mysql技术内幕sql编程^^^mysql内核i

2、bgsave

当某个客户端发送bgsave 命令后,Redis服务器会执行fork() 函数创建一个子进程,由子进程负责rdb文件创建和写入,期间服务器不会阻塞,可以接受其他客户端的命令请求,但在fork执行阶段,服务器还会阻塞,无法接受其他客户端命令(一般时间很短!)如下图所示:

姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装_mysql持久化

? 具体流程如下:

Redis客户端执行bgsave命令 或 自动触发bgsave命令;父进程先判断:当前是否在执行save,或bgsave/bgrewriteaof(aof文件重写命令)的子进程如果存在:则父进程直接返回。 如果不存在:父进程执行fork操作创建一个子进程,fork过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令fork完毕后返回 “ Background saving started” 信息并不再阻塞父进程,并可响应其他命令子进程创建临时RDB文件,待数据写入完毕后,对原有文件进行原子替换;同时子进程退出,并发送信号给父进程表示完成rdb持久化,父进程更新统计信息

注意:bgsave/bgrewriteaof(rdb/aof) 的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。

? 简化后的流程可分为三步:

服务器接收bgsave命令,主线程需调用系统的fork()函数,构建一个子进程去操作;子线程创建好RDB文件并退出时,向父进程发送信号,告知RDB文件创建完毕;父进程接收子进程创建好的RDB文件,bgsave命令执行结束

mysql持久化_姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装

Redis服务器在处理bgsave采用子线程进行IO写入,而主进程仍然可以接收其他请求,但forks子进程是同步阻塞的,在forks子进程阶段,不能接收其他请求,如果forks一个子进程花费的时间太久(一般是很快的),bgsave命令仍然有阻塞其他客户的请求的情况发生

★ 自动触发

在以下几种情况时会自动触发生成RDB文件:

redis.conf 配置文件中达到save参数条件主从复制时,从节点要从主节点进行全量复制时也会触发bgsave,生成快照发送到从节点执行shutdown,如果没有开启aof持久化,会触发bgsave执行flushall(生成空的dump.rdb!)

特别注意:flushall是删库跑路,生成一个空的dump.rdb文件,目的是覆盖并删除上次备份产生的dump.rdb,防止重启Redis时,数据又会恢复到上一次备份的时候的数据!!

——代码/命令演示触发rdb文件自动生成

? shutdown命令

1. 首先,先删除rdb文件!

连接redis客户端使用 config get dir 命令,获取rdb文件的保存路径,再打开新的命令窗口,删除 rdb文件(也可以直接删除,确保没有此文件即可)

姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装_mysql持久化

2. 删除完rdb文件后,使用shutdown命令断开客户端连接,会自动触发rdb持久化,如下图:

姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装_mysql持久化

3. redis重启后,会重新加载dump.rdb文件的数据到内存当中,达到恢复数据目的!

mysql持久化_姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装

? redis.conf配置文件save参数

redis.conf 配置文件不懂的,可以翻阅我之前redis栏目中关于配置文件的详解,附有每个参数的中文详细说明!

# 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
# 语法:save  
?
save 900 1       # 900秒(15分钟)内至少1个key值改变
save 300 10      # 300秒(5分钟)内至少10个key值改变
save 60 10000    # 60秒(1分钟)内至少10000个key值改变

1. 跟 shutdown 命令展示的一样,先删除dump.rdb文件!再打开redis.conf 文件进行如下配置:

姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装_mysql持久化

2. 打开redis服务,并用命令连接客户端,设置3个key,让其触发save条件

mysql持久化_杜蕾斯持久装和杰士邦持久装_姜承尧mysql技术内幕sql编程^^^mysql内核i

3. 等待60秒后,查看dump.rdb文件目录:

mysql持久化_杜蕾斯持久装和杰士邦持久装_姜承尧mysql技术内幕sql编程^^^mysql内核i

4.等dump.rdb文件生成完毕后,断开客户端,并再次重连客户端,测试数据是否恢复!

姜承尧mysql技术内幕sql编程^^^mysql内核i_mysql持久化_杜蕾斯持久装和杰士邦持久装

? flushall命令

flushall 生成的是空dump.rdb文件,目的是覆盖并删除上次备份的快照,防止redis重启时,自动载入并恢复到上一份备份数据!

杜蕾斯持久装和杰士邦持久装_mysql持久化_姜承尧mysql技术内幕sql编程^^^mysql内核i

? 思考:可以每秒做一次快照吗?

在未达到save的触发条件,此时服务器宕机,则会丢失最后一次同步的时间段内的数据,那是否可以设置save,每几秒或每秒就触发备份条件,不就能大大减少数据的丢失吗?

bgsave执行不阻塞主线程,但频繁地执行全量快照,也会带来两方面的开销:

磁盘压力大,多个快照竞争有限的磁盘带宽,前一个快照还未做完,后一个又开始了,易造成恶性循环bgsave 子进程需通过 fork 操作从主线程创建出来。子进程在创建后不会再阻塞主线程,但fork执行过程本身会阻塞主线程,如果频繁 fork 出 bgsave 子进程,就会频繁阻塞主线程

RDB边备份边接受其他写请求的原理?

bgsave备份时如数据量太大,内存中的数据同步到硬盘的过程会持续比较长的时间,在这段时间Redis服务一般还会收到其他客户端的写操作请求。那么如何保证数据一致性呢?

RDB中的核心思路是Copy-on-Write

在正常的快照操作中,Redis主进程会fork一个子进程来处理rdb文件的写入,此时Redis服务还会接受其他客户端包括写请求在内的任何命令。

在子进程执行备份阶段,和redis主进程接受其他客户端写请求阶段,这段时间发生的数据变化会以副本的方式存放在另一个新的内存区域,待快照操作结束后才会同步到原来的内存区域。

★ 工作流程如下所示:

杜蕾斯持久装和杰士邦持久装_mysql持久化_姜承尧mysql技术内幕sql编程^^^mysql内核i

mysql持久化_杜蕾斯持久装和杰士邦持久装_姜承尧mysql技术内幕sql编程^^^mysql内核i

?解析:

如果主线程对A是读操作,那么主线程和 bgsave 子进程相互不影响如果主线程对C是写操作,那么C就会被复制到另一块内存中,生成副本C,bgsave子进程会副本C的数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据C。

什么是AOF?

AOF(append only file)持久化:与RDB存储某个时刻的快照不同,AOF是将客户端的每一个写操作命令都记录到日志中,追加到后缀为aof 的文件末尾,在Redis服务器重启时,会加载并运行aof文件的所有命令,以达到恢复数据的目的。

AOF文件是怎么生成的呢?

★手动触发,如下图所示:

姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装_mysql持久化

★自动触发

默认情况下,Redis是没有开启AOF的,可以通过配置redis.conf文件来开启AOF持久化:

#---------------------------1.写入---------------------------
# appendonly参数开启AOF持久化  no:关闭(默认)  yes:开启
appendonly no
 
# 指定aof文件名,默认为appendonly.aof
appendfilename "appendonly.aof"
 
 
# AOF持久化三种同步策略:
#   1) no:Redis服务器不负责写入aof,由操作系统来处理何时写入aof。性能好,不安全(不推荐)
#   2) always:每个写操作都会追加到appendonly.aof,可靠性高,数据基本不丢失(慢,安全)
#   3) everysec:每秒同步一次到appendonly.aof,会导致丢失这1s数据(折中选择,默认值)
appendfsync everysec

AOF工作流程图解如下:

mysql持久化_杜蕾斯持久装和杰士邦持久装_姜承尧mysql技术内幕sql编程^^^mysql内核i

? 解析:

所有的写命令追加到aof_buf缓冲区中。AOF会根据对应的策略向磁盘做同步操作。刷盘策略由appendfsync参数决定。

AOF持久化策略的效率与安全性:

★ 命令演示操作

1. 首先打开redis.conf 配置文件,在附加模式这栏中,修改appendonly属性为yes

配置文件设置完毕后,记得重启redis服务 !!!或者先关闭服务才修改配置文件!

杜蕾斯持久装和杰士邦持久装_姜承尧mysql技术内幕sql编程^^^mysql内核i_mysql持久化

2. 把目录下的aof 和 rdb文件都删除掉(不知道目录的可以通过config get dir 获取)

杜蕾斯持久装和杰士邦持久装_mysql持久化_姜承尧mysql技术内幕sql编程^^^mysql内核i

姜承尧mysql技术内幕sql编程^^^mysql内核i_mysql持久化_杜蕾斯持久装和杰士邦持久装

3. 重启服务后,重新连接客户端,执行写入操作

杜蕾斯持久装和杰士邦持久装_姜承尧mysql技术内幕sql编程^^^mysql内核i_mysql持久化

4. 查看appendonly.aof 文件!

每一个写入命令都被追加到aof文件中!

姜承尧mysql技术内幕sql编程^^^mysql内核i_mysql持久化_杜蕾斯持久装和杰士邦持久装

?思考:如何恢复数据?

其实想要从这些文件中恢复数据,只需要重新启动Redis即可。如图:

姜承尧mysql技术内幕sql编程^^^mysql内核i_mysql持久化_杜蕾斯持久装和杰士邦持久装

? 解析:在上图中,通过重启服务,达到恢复数据的目的,成功获取到了数据!

——那么恢复后,我们继续执行set 写入操作呢?再次查看 aof 文件:

姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装_mysql持久化

结论:可以看到,写入操作又被添加到aof 的末尾,而之前已经恢复的数据的写入命令也还在!

持久化恢复数据

数据的备份、持久化做完了,我们如何从这些持久化文件中恢复数据呢?如果一台服务器上有既有RDB文件,又有AOF文件,该加载谁呢?如下图:

? 解析:启动时会先检查AOF文件是否存在,如果不存在就尝试加载RDB

那么为什么会优先加载AOF呢?因为AOF保存的数据更完整,通过上面的分析我们知道AOF基本上最多损失1s的数据。

什么是AOF重写?

开启AOF后,每一次写操作都会被追加到appendonly.aof,随着时间推移,AOF文件会越来越大。数据恢复也更加慢。如果不加以控制,会对Redis服务器,甚至对操作系统造成影响。为了解决AOF文件体积膨胀的问题,Redis提供AOF文件重写机制来对AOF文件进行“瘦身”。

★ 举例说明:比如对一个key多次执行incr命令

incr num 1
incr num 2
incr num 3
incr num 4
...
incr num 100000

aof 模式会把每次命令都追加到appendonly.aof 文件中,文件过大,redis服务器重启恢复数据时,就会非常慢。如果此时对aof文件重写,可以生成一个恢复当前数据的最少命令集,比如上面的例子中那么多条命令,可以重写为:

set?num?100000

▎如何触发重写机制?

自动触发:在redis.conf配置文件中,进行配置如下参数:

#---------------------------2.重写---------------------------
# 触发重写配置
# 当AOF文件的体积>64MB,且文件的体积比上次重写后的体积大了一倍(100%),会执行bgrewriteaof
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

 
# 如aof文件被损坏,是否开启自动修复
aof-load-truncated yes

如果要手动触发,可直接调用bgrewriteaof命令

★ 图解触发重写机制后的整体流程如下:

杜蕾斯持久装和杰士邦持久装_姜承尧mysql技术内幕sql编程^^^mysql内核i_mysql持久化

流程如下:

判断当前是否存在正在执行 bgsave/bgrewriteaof的子进程,如果存在则bgrewriteaof命令直接返回,如果存在bgsave命令则等bgsave执行完成后再执行。父进程执行fork操作创建子进程,fork过程中父进程是阻塞的(等同RDB中的bgsave),fork完毕后父进程才能继续响应其他客户端的请求重写采用写入时复制,父进程fork出一个子进程,并把父进程那一时刻的内存数据拷贝一份副本给子进程,子进程对着这份副本数据逐一写入到新aof文件,子进程重写过程中,由于父进程还可响应其他客户端命令,因此子进程重写期间,父进程接受的新的写命令都存到AOF重写缓冲区aof_rewrite_buf中,防止新AOF文件生成期间丢失这部分数据。所以Redis的写命令同时追加到aof_buf 和 aof_rewirte_buf 两个缓冲区。子进程写完新的AOF文件后,向父进程发送完成信号父进程再把AOF重写缓冲区aof_rewrite_buf的数据,追加写入到子进程已完成的新AOF文件中,保证新AOF文件所保存的数据库状态和服务器当前状态一致。使用新的AOF文件替换老文件,完成AOF重写。

思考:父进程为什么同时向aof_buf 和 aof_rewrite_buf两个缓冲区写入数据?

答:在AOF重写日志期间发生宕机(即子进程重写过程中发生意外),新aof文件还没来得及替换旧的,故恢复数据时,用的还是旧的aof文件,即写入aof_buf 缓冲区的数据。

aof_rewrite_buf 缓冲区:重写期间,由于父进程还可接受其他客户端的写请求,新接受的写请求,需放到重写缓存区aof_rewrite_buf中,子进程重写完毕,父进程会把aof_rewrite_buf重写缓冲区的数据追加到新aof文件中,确保数据的统一

★ 简化后的流程如下:

姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装_mysql持久化

AOF的rewrite重写 和 RDB的bgsave命令 都是由父进程fork 出一个子进程来执行重写是直接把当前内存的数据生成对应命令,并不是读取旧AOF文件进行命令合并

?思考:重写过程中,主进程哪些地方会阻塞?

?思考:为什么AOF重写不复用旧AOF文件

AOF文件损坏怎么办?

redis 启动服务如果存在aof 文件就不会去加载rdb 文件了,而是以aof 文件来恢复数据,如果aof 文件损坏了,那会出现什么样的结果呢?

通常aof 文件错误有两种:

写入错误:在写入的过程中,出现写入操作被打断,磁盘空间不足,或者磁盘出现物理故障等问题。

停机故障:在写入的过程中,发生 Redis 服务器进程别强制杀死,或者服务器的宿主机器被强制停机等情况

★ 假设我们对已经存在的aof 文件做人工损坏

1. 使用 vim 命令进入编辑aof 文件

我是Mac系统用的终端(命令同Linux系统),所以下面讲解就用命令方式操作,windowns用户用cmd,或者找到目录直接鼠标右键打开

mysql持久化_姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装

2. 按 “ i ” 键进入编辑模式,出现 insert 字样表示当前可编辑

mysql持久化_姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装

3. 将正确的值搞乱之后,再退出保存如下:

姜承尧mysql技术内幕sql编程^^^mysql内核i_mysql持久化_杜蕾斯持久装和杰士邦持久装

4. 切换到redis 客户端,断开连接,重连服务和客户端

杜蕾斯持久装和杰士邦持久装_mysql持久化_姜承尧mysql技术内幕sql编程^^^mysql内核i

? 解析:如上图所示,提示连接失败!如果在启动 Redis 时, 用户试图将带有不完整数据的 AOF 文件提供给 Redis 时, Redis 将会拒绝使用用户所提供的 AOF 文件来还原数据, 因为错误的 AOF 文件会破坏数据的一致性。

?那么针对数据不完整aof 文件如何修复呢?

Redis 附带的redis-check-aof可以解决这类因为 AOF 出错而无法启动的问题: 它可以移除 AOF 文件所包含的不完整的数据, 使得 Redis 可以重新载入 AOF 文件中的数据。

1、具体操作步骤如下:

姜承尧mysql技术内幕sql编程^^^mysql内核i_杜蕾斯持久装和杰士邦持久装_mysql持久化

程序会定位到出错的地方, 并询问用户, 是否要对文件进行截断, 如果选择y的话, 就会对指定的 AOF 文件进行修复。

2、修复成功之后,我们再来用vim 命令查看aof 文件的内容

mysql持久化_杜蕾斯持久装和杰士邦持久装_姜承尧mysql技术内幕sql编程^^^mysql内核i

可以看到,出错的原因 —— 那个不完整的SET命令已经被删除了。

3、测试结果

RDB vs AOF

方式

RDB

AOF

启动优先级

体积

恢复速度

数据安全性

会丢数据

由策略决定

轻重

RDB的优缺点有哪些?

?优点

? 缺点

AOF的优缺点有哪些?

?优点

? 缺点

如何选择RDB和AOF? 总结

? RDB持久化

在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

RDB 是存放数据库中数据,适合做数据备份,但数据可能不全,最近几分钟的数据可能没有

? AOF持久化

以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录。

每秒执行一次,如果有写操作的命令就存储起来,最多丢失1秒的数据,适合做数据恢复。不适合做数据备份,由于每秒都会执行多少会抢占redis的内存,影响性能。

(编辑:应用网_丽江站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章