Redis学习:
https://blog.youkuaiyun.com/2301_80220607/category_13051025.html?spm=1001.2014.3001.5482
目录
1. 认识持久化
学习过mysql的都知道,mysql事务有四个非常重要的特性:原子性、一致性、持久性、隔离性。这里的持久性就是我们这里要说的持久化了,那么MySQL是如何做到数据的持久性的呢?方法就是将数据存储到磁盘中去,那么redis能否这样做呢?
我们前面一直在说,Redis之所以能够做到快速的对数据进行操作,原因就是它是将数据存储在内存中的,如果我们将数据存储到磁盘上,那么Redis不就会丧失它的特性了嘛。但并不意味着我们的Redis就无法进行持久化操作了,我们采取的方法是:在内存和磁盘上各存储一份。
磁盘上的那份只负责:当我们打开Redis时,对我们之前的数据进行恢复;平时的使用还是直接在内存中进行。这样的缺点就是消耗的空间变多了,因为我们相当于要写入两份数据(内存和磁盘),但总体来说还是问题不大的,因为磁盘价格很低
Redis支持RDB (Redis DateBase) 和AOF (Append Only File) 两种持久化机制,具体到方法上来说就是支持定期备份和实时备份两种方法。
2. RDB机制
RDB机制就是按照定期备份的方法来对我们的数据进行持久化的。Redis定期的把内存中的数据全部写入磁盘中,生成一个快照,后续重启Redis的时候,虽然我们内存中的数据已经没了,但是我们可以通过磁盘中的快照文件恢复原来的数据
2.1 触发机制
定期备份的触发方式有两种:手动触发和自动触发
手动触发分别对应save和bgsave命令
- save命令:阻塞当前进程,直到RDB过程完成为止,对于内存比较大的实例是比较灾难的,因为Redis是单线程的,所以我们一般不使用这个命令
- bgsave命令:Redis进程执行fork创建子进程,子进程来完成Redis的持久化工作,完成后自动结束。这种方法阻塞只发生在fork阶段,时间比较短
除了手动触发外,Redis运行自动触发Redis备份机制,这种机制在实战中更有意义
- 使用save配置:如“save m n”表示m秒内,数据集发生了n次修改,就自动RDB持久化
- 从节点进行全量复制操作时,主节点自动进行RDB持久化,随后将RDB文件内容发送给从节点
- 执行shutdown命令关闭Redis时,执行RDB持久化
2.2 bgsave命令的流程说明

- 执行bgsave命令,Redis判断现在是否有正在执行的子进程,比如RDB/AOF子进程,如果有,bgsave直接退出
⽗进程执⾏ fork 创建⼦进程,fork 过程中⽗进程会阻塞,通过 info stats 命令查看latest_fork_usec 选项,可以获取最近⼀次 fork 操作的耗时,单位为微秒。 ⽗进程 fork 完成后,bgsave 命令返回 "Background saving started" 信息并不再阻塞⽗进程,可以继续响应其他命令。 ⼦进程创建 RDB ⽂件,根据⽗进程内存⽣成临时快照⽂件,完成后对原有⽂件进⾏原⼦替换。执 ⾏ lastsave 命令可以获取最后⼀次⽣成 RDB 的时间,对应 info 统计的 rdb_last_save_time 选 项。 进程发送信号给⽗进程表⽰完成,⽗进程更新统计信息。
3. RDB文件
查看:
首先我们先来看一下我们如何找到RDB文件并进行查看,我们可以先看一下我们Redis的配置文件redis.conf,在/etc/redis这个目录下

我们使用vim进入这个配置文件后,可以查看我们的工作目录为/var/lib/redis

我们的RDB文件就存在于这个工作目录之下:

这个文件我们可以使用vim进行查看,但里面由于是二进制压缩过的,所以我们可以看到里面是乱码。
切记不要手动修改dump.rdb文件中的内容,修改了可能会导致我们的redis无法正确启动
检验:
如果你不小心对dump.rdb文件中的内容做出了修改,或者由于某些原因Redis加载dump.rdb文件时失败了而拒绝启动。这时可以使用Redis提供的redis-check-dump工具检测RDB文件并获取对应的检测报告

我们的Redis是按照一定的规则定期生成RDB文件的,那么具体的生成规则是什么呢?
当执行生成RDB镜像时,我们把要写的快照信息先写入一个临时文件,当这个快照生成完毕之后,我们把原来的RDB文件删除,然后将这个临时文件改名为dump.rdb,所以说RDB文件自始至终只能有一个
4. RDB效果演示
4.1 手动触发

我们同时打开多个终端,一个打开redis进行操作,另一个可以查看dump.rdb文件观察数据的持久化情况
我们可以看到此时我们的redis中是没有数据的,如果此时我们向我们的redis中插入几个值,我们的dump.rdb文件会有变化吗?


插入后我们会发现我们的RDB文件中的内容并没有发生变化,原因是什么呢?其实就是我们前面所说的,RDB机制并不是实时备份的,需要我们手动触发或者满足一定的条件后自动触发
手动触发的方式我们上面讲了是有save和bgsave两种命令:
下面我们看一下具体如何操作


如图我们又添加了两个新的值key3和key4,然后我们使用bgsave手动触发RDB机制,然后此时查看我们的RDB文件,通过一些关键字我们就可以看到我们的值已经被备份到RDB文件中了,此时如果我们重启了redis服务器,RDB文件就会被加载恢复内存之前的状态
在手动触发查看RDB文件时,有一个需要注意的小细节是:这里我们新写入的数据很少,所以手动触发后直接查看RDB文件就可以发现新写的数据已经进行了备份,但是如果我们写入的数据很多,此时再使用bgsave手动触发的时候,子进程就需要更多的时间在后台完成备份,此时我们短时间内就去查看我们的RDB文件可能就会因为还没备份完而看不到新写入的数据
这里我们再讲解一下save和bgsave的区别:
我们上面提到save和bgsave最大的不同就是一个是阻塞当前进程进行备份的,一个是创建子进程进行备份的,那么具体是如何工作的呢?我们可以结合实例看一下
由于我们数据过少的原因,bgsave创建子进程后,子进程的持久化过程是非常快的,所以我们很难通过观察进程的方式来看我们是否创建了一个子进程,但是我们上面还提到的一个东西就是:子进程会将生成的快照先写入一个临时文件中,全部备份完之后会将原RDB文件删除,然后将新生成的文件改名成dump.rdb,所以我们可以通过观察RDB文件inode的方法,来查看bgsave之后是否生成了新的文件,从而来判断我们bgsave是否手动触发成功(save命令是阻塞当前进程,然后将快照直接写入原RDB文件中的,不会生成新的RDB文件)
我们可以使用stat filename命令查看文件的inode
起初RDB文件的inode:

bgsave命令执行后:

我们可以发现RDB文件的inode发生了变化,也就是说这个RDB文件并不是原来的RDB文件了,这就侧面说明我们的bgsave正确完成了备份工作(临时文件+重命名机制)
4.2 自动触发
RDB机制也可以自动触发
我们先来看这样一个现象,当我们往Redis中写入几个值的时候,此时我们如果直接退出Redis(注意要使用ctrl+c,不要使用quit),我们会发现我们刚刚写入的值并没有成功备份,但是如果此时我们重启Redis服务器后(systemctl restat redis-server),再查看dump.rdb文件就会发现已经备份了


这就可以验证我们上面所说的一种自动触发的情况:执行shutdown命令关闭Redis时,执行RDB持久化(正确关闭重启Redis服务器时,也会自动触发)
但是如果我们不是正确退出服务器,而是异常重启(kill -9 或者 服务器断电),此时Redis服务器就会来不及生成 rdb,内存中尚未保存到快照中的数据,就会随着服务器的重启而丢失


如图1,我们 ctrl+c 退出redis后,然后杀死redis服务器,我们会发现redis服务器很快就会自动重启,这是因为Redis是作为系统服务安装的,它被配置为系统服务,由系统的进程管理器(如systemd 或 init)监控和管理。当Redis进程异常退出时,进程管理器会自动重启它。
按照我们上面讲的,异常退出后我们之前插入的数据并没有生成快照保存到RDB文件中,从图二也可以看出我们的RDB文件中并没有 key1 key2,那么为什么我们的redis进程自动重启后,我们进入查看仍能看到之前的数据呢?这实际上是另一种持久化机制---AOF 在发力,下面会讲解
除了这种自动触发方式外,我们上面还提到有两种触发方式:从节点全量复制的时候和满足配置文件配置选项的时候
从节点全量复制也属于一个比较大的话题,后面会讲,现在我们来看一下配置文件这种情况
我们可以查看redis的配置文件(路径:/etc/redis/redis.conf)

里面有这几项内容,最上面的注释语句就是告诉我们 save 配置的规则是:save m n,意思是在m秒内修改n次就会自动触发RDB,最下面的三条就是我们的默认的 save 配置,save " " 的意思是不启用 save 配置(关闭自动生成快照)
4.3 RDB文件的检测
在上面的时候我们就讲到,当我们的 rdb 文件发生损坏的时候,我们的 redis 服务器可能就会无法正常启动,下面我们来故意修改一下 rdb 文件来看一下具体会出什么错误,以及我们如何检测我们出现的错误

我们手动的删除 rdb 文件中间的一些内容,然后再重新启动一下服务器(注意要用 kill -9 的方式启动服务器,因为如果我们使用 systemctl restart redis-server ,会自动触发RDB,然后生成新的RDB 文件)

(注意上面的操作是在关闭了aof机制下进行的,aof机制不关闭的话可能无法观察到这种情况
此时 redis 服务器无法工作的时候,我们可以看看 redis 日志,了解一下发生了什么
redis 日志文件所在的路径为:/var/log/redis(这个路径也是在配置文件中配置的)


我们的RDB文件的修改是在最近一次操作的,所以直接查看最后一条日志就是了,这条日志就告诉我们在 rdb 数据恢复中出现了错误
除了查看日志外,我们也可以通过 redis 提供的检测工具来检查我们的 RDB 文件是否有错误

我们使用这个检测工具也非常简单,在这个命令后加上文件名即可

注意要在RDB文件的工作目录下执行这个检测语句才行,执行后我们也可以看到我们的 RDB 文件是有错误的
观看到之后修复 RDB 文件也很简单,正确重启一次即可(systemctl restart redis-server)
5. RDB机制总结
- RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据快照。非常适用于备份,全量复制等场景,比如每6个小时执行bgsave备份,并把 RDB 文件复制到远程机器或者文件系统中(如 hdfs)用于灾备。
- Redis 加载 RDB 恢复数据远远快于 AOF 的方式,原因:RDB 使用的二进制的方式来组织数据,直接把数据读取到内存中,按照字节的格式取出来,放到结构体/对象中即可;而 AOF 是使用文本的方式来组织数据的,需要进行一系列的字符串切分操作。
- RDB 方式数据没办法做到实时持久化/秒级持久化。因为 bgsave 每次运行都要执行 fork 创建子进程,属于重量级操作,频繁执行成本过高。
- RDB 文件使用特定二进制格式保存,Redis 版本演进过程中有多个 RDB 版本,兼容可能有风险。老版本的 redis 的 RDB 文件放到新版本的 redis 中不一定能识别,如果遇到要升级版本,且遇到了不兼容的问题,可以通过写一个程序的方式,直接遍历旧的 redis 中的所有key,把数据取出来之后,插入到新的 redis 服务器中即可。
6. AOF机制
AOF(Append Only File)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主要方式。
7. AOF的基本使用
AOF 机制有点类似于 mysql 的 binlog,会把用户的所有操作都记录下来写入文件中。当Redis重启的时候就可以读 aof 文件中的内容恢复数据。
AOF 默认是关闭的,如果要开启需要修改配置文件,而且 AOF 开启后,RDB 就会失效。

修改后重启服务器就奏效了
图片中下面这个就是我们的 aof 文件的文件名,这个文件与 RDB 文件是存放在同一个目录下的(/var/lib/redis)
AOF 是一个文本文件,每次进行的操作都会被记录到文本文件中,通过一些特定的符号来对文件进行分割,要知道分割也是按照一定规则的,具体的分割规则我们这里就不做讲解了

8. AOF 的效率和同步问题
8.1 AOF 的效率问题
我们都知道 Redis 之所以操作起来速度比较快的原因,就是因为它是对内存进行操作的,现在我们的 AOF 要求我们同时往内存和硬盘上写数据,那么工作效率还能得到保障吗?
答案是:没有影响!!原因是:
- AOF 机制并非直接让工作线程把数据写入到磁盘当中,而是先写入一个内存缓冲区中的,当缓冲区满了再刷新到磁盘中去,硬件交互的工作是最耗时的,这样减少了访问硬件的次数就提高了效率
- 磁盘顺序读写的速度要明显快于随机访问,AOF 机制每次把最新的操作都写入到文件末尾,属于顺序写入
8.2 AOF 的同步问题
当然,这种先写入内存缓冲区的方法也不全是好处,这样说到底还是先写在内存中的,此时如果我们的机器出现了问题,比如断电等情况就会让我们的服务异常退出,那么我们内存缓冲区中的数据就会还没来得及刷新到磁盘中就丢失,我们使用 AOF 机制想实现的实时更新的目的就没能达到
所以基于上面所讲,我们就可以得出缓冲区的刷新策略:
- 刷新频率越高,性能影响就越大,但是数据可靠性就更高
- 刷新频率越低,性能影响就越小,但是数据可靠性就更低
Redis 提供了多种 AOF 缓冲区文件同步策略,由参数appendfsync控制,有多种可配置选项
|
可配置值
|
说明
|
|---|---|
|
always
|
命令写⼊ aof_buf 后调⽤ fsync 同步,完成后返回
|
|
everysec
|
命令写⼊aof_buf 后只执⾏ write 操作,不进⾏
fsync。每秒由同步线程进⾏ fsync。
|
| no |
命令写⼊ aof_buf 后只执⾏ write 操作,由 OS 控制
fsync 频率。
|
一般选择的第二个配置项

系统调用 write 和 fsync 说明:
-
write 操作会触发延迟写(delayed write)机制。Linux 在内核提供⻚缓冲区⽤来提供硬盘 IO 性能。write 操作在写⼊系统缓冲区后⽴即返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区⻚空间写满或达到特定时间周期。同步⽂件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
-
Fsync 针对单个⽂件操作,做强制硬盘同步,fsync 将阻塞直到数据写⼊到硬盘。
-
配置为 always 时,每次写⼊都要同步 AOF ⽂件,性能很差,在⼀般的 SATA 硬盘上,只能⽀持⼤约⼏百 TPS 写⼊。除⾮是⾮常重要的数据,否则不建议配置。
-
配置为 no 时,由于操作系统同步策略不可控,虽然提⾼了性能,但数据丢失⻛险⼤增,除⾮数据重要程度很低,⼀般不建议配置。
-
配置为 everysec,是默认配置,也是推荐配置,兼顾了数据安全性和性能。理论上最多丢失 1 秒的数据。
9. AOF 的重写机制
随着命令不断写入 AOF 文件,文件会越来越大,为了解决这个问题,Redis 引入了 AOF 重写机制压缩文件体积。AOF 文件重写是把 Redis 中的数据转化为写命令同步到新的 AOF 文件中去。
重写的 AOF 文件为什么可以变小。有以下几个原因:
- 进程内超时的数据不再写入文件
-
旧的 AOF 中的⽆效命令,例如 del、hdel、srem 等重写后将会删除,只需要保留数据的最终版本。
-
多条写操作合并为⼀条,例如 lpush list a、lpush list b、lpush list 从可以合并为 lpush list a bc。
较小的 AOF 文件一方面减少了硬件空间的使用,另一方面提供了启动 Redis 数据恢复的速度
AOF 重写机制可以分为手动触发和自动触发:
- 手动触发:调用 bgrewriteaof 命令
- 自动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数决定自动触发时机。auto-aof-rewrite-min-size:表⽰触发重写时 AOF 的最⼩⽂件⼤⼩,默认为 64MB。auto-aof-rewrite-percentage:代表当前 AOF 占⽤⼤⼩相⽐较上次重写时增加的⽐例。
下面我们来看一下 AOF 具体的重写流程

1. 执行 AOF 重写请求
如果当前进程正在执行 AOF 重写,请求不执行。如果当前进程正在执行 bgsave 命令,重写命令延迟到 bgsave 结束后再执行
2. 父进程执行fork 创建子进程
3. 重写
-
主进程 fork 之后,继续响应其他命令。所有修改操作写⼊ AOF 缓冲区(aof_buf)并根据 appendfsync 策略同步到硬盘,保证旧 AOF ⽂件机制正确。
-
⼦进程只有 fork 之前的所有内存信息,⽗进程中需要将 fork 之后这段时间的修改操作写⼊AOF 重写缓冲区(aof_rewrite_buf)中。
4. 子进程依据内存快照,将内容合并到新的 AOF 文件中去
5. 子进程完成重写
-
新⽂件写⼊后,⼦进程发送信号给⽗进程。
-
⽗进程把 AOF重写缓冲区内(aof_rewrite_buf)临时保存的命令追加到新 AOF ⽂件中。
-
⽤新 AOF ⽂件替换⽼ AOF ⽂件。
10. 启动时数据恢复
上面就是我们的两种重启机制,下面我们来看一下我们的数据如何在服务重启时完成恢复:

11. 总结
回顾:
-
Redis 提供了两种持久化⽅案:RDB 和 AOF。
-
RDB 视为内存的快照,产⽣的内容更为紧凑,占⽤空间较⼩,恢复时速度更快。但产⽣ RDB 的开销较⼤,不适合进⾏实时持久化,⼀般⽤于冷备和主从复制。
-
AOF 视为对修改命令保存,在恢复时需要重放命令。并且有重写机制来定期压缩 AOF ⽂件。
-
RDB 和 AOF 都使⽤ fork 创建⼦进程,利⽤ Linux ⼦进程拥有⽗进程内存快照的特点进⾏持久化,尽可能不影响主进程继续处理后续命令。
感谢各位大佬观看,创作不易,还望各位大佬点赞支持!!!
2776

被折叠的 条评论
为什么被折叠?



