自学内容网 自学内容网

redis 持久化

AOF日志

Redis每执行一条写操作命令,就把该命令以追加的方式写到一个文件里,然后重启Redis时,先去读取这个文件里的命令,并且执行他,这就相当于恢复了数据
在这里插入图片描述
这就是Redis中的AOF(Append Of File)持久化功能,只会记录写操作命令
AOF日志文件其实就是普通的文本,不过里面的内容如果不知道规则的话应该是看不懂的。
以[set name xiaolin]为例,Redis执行了这条命令后,记录在AOF日志的内容如下图
在这里插入图片描述
*3表示当前命令有三部分,每行以$+数字开头,数字表示这部分的命令,键或值一共有多少字节

Redis是先执行写操作命令后才将该命令记录到AOF日志中的

  1. 避免额外的检查开销:不需要额外的检查命令的语法正确性
  2. 不会阻塞当前写操作的命令行:因为写操作执行成功后才会写入

当然这也有坏处

  1. 可能会有数据丢失的风险
  2. 可能会给下一条命令带来阻塞

因为执行命令和写日志都是在主进程完成的,也就是说两个操作是同步的
在这里插入图片描述
如果写日志时磁盘的I/O压力很大,就会导致写硬盘的速度很慢,进而阻塞住,也就会导致后面的命令无法执行。

这都和AOF日志写回硬盘的时机有关

  1. Redis执行完写操作命令后,会将命令追加到server.aof_buf缓冲区
  2. 然后通过write()系统调用,将aof_buf缓冲区的数据写入到AOF文件,此时数据还没有到硬盘,而是拷贝到了内核缓冲区page cache
  3. 具体内核缓冲区的数据什么时候写入到硬盘,由内核决定。

三种回写策略

  • Always:每次执行完写操作后,同步将AOF日志数据写回硬盘
  • Everysec:每隔一秒将缓冲区的内容写回硬盘
  • No:由操作系统决定

这三种策略只是在控制fsync()函数的调用时机
在这里插入图片描述

AOF重写机制

随着写操作越来越多,文件的大小会越来越大,此时重启Redis后整个文件的恢复就会很慢
为了避免AOF越写越大,提供了AOF重写机制。即读取当前数据库的所有键值对,然后每一个键值用一条命令记录到[新的AOF文件中],等到全部记录完成后,就将新的AOF文件替换掉现有的AOF文件。

不用现有的AOF文件是因为,如果AOF重写失败了,现有的AOF文件就会被污染,用新的重写失败了直接删除即可。

AOF后台重写

如果AOF过大,重写过程过于耗时,所以Redis的重写AOF过程是由后台子进程bgrewriteof来完成的

  1. 主进程可以继续处理请求,避免阻塞
  2. 使用子进程而不是子线程,是因为多线程会共享内存,就需要加锁来保证数据的安全,会降低性能。

子进程是怎么拥有和主进程一样的数据副本的
os会将主进程的页表复制一份给子进程,两者的虚拟空间不同,但是对应的物理地址是同一个
在这里插入图片描述
节约物理内存资源,页表对应的页表项的属性会标记该物理内存的权限是只读

不过,当父进程或子进程在这个内存发起写操作时,CPU就会触发写保护中断,然后os会在[写保护中断处理函数]里进行物理内存的复制,并重新设置其内存映射关系,将父子进程的内存权限设置为可读写,最后才会对内存进行写操作,这个过程被称为[写时复制(Copy On Write)]
在这里插入图片描述

有两个阶段会导致阻塞父进程:

  1. 创建子进程的途中,由于需要复制父进程的页表等数据结构,阻塞时间和页表大小有关
  2. 创建完子进程之后,如果发生了写时复制,这期间会拷贝物理内存,阻塞时间与物理内存大小有关

在子进程重写过程中,主进程依然可以正常处理命令,如果此时修改了已经存在的key-value就会发生写时复制(只会修改被修改的那个一个)
还有一个问题,此时修改了数据后,子进程的内存数据和主进程不一致了怎么办呢?

为了解决这个问题,redis设置了一个AOF重写缓冲区,这个缓冲区在bgrewriteof子进程之后开始使用

在重写AOF期间,当Redis执行完一个写命令之后,他会同时将这个写命令写入到[AOF缓冲区]和[AOF重写缓冲区]。
在这里插入图片描述

也就是说在bgrewriteof期间,主进程需要执行以下三个工作:

  • 客户端发来的命令
  • 追加到AOF缓冲区
  • 追加到AOF重写缓冲区

当子进程完成工作后会向主进程发送一条信号,主进程收到这条信号后,会调用一个信号函数做以下工作:

  • 将AOF重写缓冲区的所有内容追加到AOF的文件中
  • 新的AOF的文件进行改名,覆盖现有的AOF文件

信号函数执行完成后,主进程就可以和往常一样处理命令了。

RDB快照

所谓的快照,就是记录某一个瞬间的东西,RDB记录的是实际数据,因此Redis在恢复数据时,RDB恢复数据的效率会比AOF高些,因为直接将RDB读入内存即可。

Redis提供了两个命令来生成RDB文件,分别是savebgsave他们的区别就是是否在主线程里执行

  • save命令:会在主线程生成RDB文件,会阻塞主线程
  • bgsave命令:会创建一个子进程来生成RDB文件,避免主线程的阻塞

RDB文件的加载工作是在服务器启动时执行的,Redis并没有提供专门用于加载RDB文件的命令。
Redis还可以通过配置文件的选项每隔一段时间执行一次bgsave命令,默认会提供以下配置:

save 900 1
save 300 10
save 60 10000

名字虽然叫save,但是实际执行的是bgsave,只要满足上面条件的任意一个就会执行bgsave:

  • 900秒内,对数据库进行了至少一次修改
  • 300秒内,对数据库进行了至少十次修改
  • 60秒内,对数据库进行了至少10000次修改

Redis的快照是全量快照,也就是每次执行快照,都是把内存中的[所有数据]都记录到磁盘中。所以可以认为执行快照是一个比较重的操作。

通常设置至少五分钟才保存一次快照,因此在服务器发生故障时,丢失的数据会比AOF多很多。

在bgsave时候,主进程还是可以继续进行写操作的,原理依然是写时复制,不过这次RDB只会记录原本的记录,更新后的需要下一次RDB才可以记录。

RDB与AOF合体

尽管RDB比AOF的数据恢复速度快,但是快照的频率不好把握。

  • 频率太低,数据丢失较多
  • 频率太高,会带来额外的性能开销

混合使用AOF日志和内存快照,也叫混合持久化,工作在AOF日志重写过程

在AOF重写日志时,fork出来的重写子进程会先将与主线程共享的内存数据以RDB的方式写入到AOF文件,然后主线程处理的操作命令会被记录在重写缓冲区中,其会以增量命令以AOF方式写入到AOF文件,写入完成后通知主进程将新的含有RDB格式和AOF格式的AOF文件替换旧的AOF文件

也就是说,AOF文件的前半部分是RDB格式的全量数据,后半部分是AOF格式的增量数据
在这里插入图片描述
这样的好处在于重启Redis时,由于前半部分是RDB,这样加载的时候速度很快,随后才会加载后半部分AOF内容,可以使得更少的数据损失。

大Key对AOF重写和RDB的影响

当使用Always策略的时候,写入大Key,主线程在执行fsync()函数的时候,阻塞时间会比较久
使用Everysec策略时,是异步执行fsync(),不会对主线程有什么影响
No策略,不会影响

大Key对AOF重写的影响

会很快触发AOF重写机制,通过fork函数创建子进程时候,虽然不会复制父进程的物理内存,但是内核会把父进程的页表复制一份给子进程,如果页表很大,复制过程比较耗时,那么执行fork函数的时候就会发生阻塞现象

由于fork是主线程调用的,因此fork发生阻塞,就意味着会阻塞主线程。


原文地址:https://blog.csdn.net/qq_73182976/article/details/143805725

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!