Redis的两种持久化机制RDB和AOF

Redis持久化机制

Redis 的⼀种持久化⽅式叫快照(snapshotting,RDB(Redis Database)),另⼀种⽅式是只追加⽂件(append-only file, AOF)

1.RDB持久化机制

RDB文件是一种经过压缩的二进制文件

RDB文件的写入和载入流程如下图所示

在这里插入图片描述

RDB文件的创建(既可以手动也可以配置选项定期执行)

RDB文件的手动创建

rdbSave 函数负责将内存中的数据库数据以 RDB 格式保存到磁盘中, 如果 RDB 文件已存在, 那么新的 RDB 文件将替换已有的 RDB 文件。

在保存 RDB 文件期间, 主进程会被阻塞, 直到保存完成为止。

SAVE 和 BGSAVE 两个命令都会调用 rdbSave 函数,但它们调用的方式各有不同:

SAVE 直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。
BGSAVE 则 fork 出一个子进程,子进程负责调用 rdbSave ,并在保存完成之后向主进程发送信号,通知保存已完成。因为 rdbSave 在子进程被调用,所以 Redis 服务器在 BGSAVE 执行期间仍然可以继续处理客户端的请求。
通过伪代码来描述这两个命令,可以很容易地看出它们之间的区别:

def SAVE():

    rdbSave()


def BGSAVE():

    pid = fork()

    if pid == 0:

        # 子进程保存 RDB
        rdbSave()

    elif pid > 0:

        # 父进程继续处理请求,并等待子进程的完成信号
        handle_request()

    else:

        # pid == -1
        # 处理 fork 错误
        handle_fork_error()

注意:

BGREWRITEAOF 和 BGSAVE (两个有子进程去执行的命令)不能同时执行(为了避免性能问题):

如果 BGSAVE 正在执行,那么 BGREWRITEAOF 的重写请求会被延迟到 BGSAVE 执行完毕之后进行,执行 BGREWRITEAOF 命令的客户端会收到请求被延迟的回复。
如果 BGREWRITEAOF 正在执行,那么调用 BGSAVE 的客户端将收到出错信息,表示这两个命令不能同时执行。
BGREWRITEAOF 和 BGSAVE 两个命令在操作方面并没有什么冲突的地方, 不能同时执行它们只是一个性能方面的考虑: 并发出两个子进程, 并且两个子进程都同时进行大量的磁盘写入操作, 这怎么想都不会是一个好主意。

因为RDB保存在硬盘里面的,所以即使Redis服务器退出,甚至运行Redis的服务器停机,只要RDB文件存在,Redis服务器就可以利用它来还原DB状态。

RDB文件自动生成的时机(在配置文件Redis.conf配置定时执行策略,然后由BGSAVE在后台生成RDB文件)

1.快照(snapshotting)持久化(RDB) :对 Redis 中的数据执行周期性的持久化

Redis 可以通过创建快照来获得存储在内存⾥⾯的数据在某个时间点上的副本。Redis 创建快照之后,可以对快照进⾏备份,可以将快照复制到其他服务器从⽽创建具有相同数据的服务器副本(Redis 主从结构,主要⽤来提⾼ Redis 性能),还可以将快照留在原地以便重启服务器的时候使⽤。

快照持久化是 Redis 默认采⽤的持久化⽅式,在 Redis.conf 配置⽂件中默认有此下配置:
在这里插入图片描述

配置文件触发执行BGSAVE的原理是dirty计数器和lastsave属性

dirty计数器记录距离上一次成功执行SAVE命令或者BGSAVE命令之后,服务器对数据库状态发生了多少次改变,也就是进行了多少次修改。
在这里插入图片描述

然后在Redis周期性操作函数serverCron默认每个100ms就会执行一次,该函数对于正在运行的服务器进行维护,它的其中一项工作就是检查save选项所配置的条件是否已经满足,也就是取遍历saveparams数组,和dirty及数字以及lastsave字段进行比较。只要任意一个条件满足,那么服务器就会执行BGSAVE函数

在这里插入图片描述

RDB载入(载入期间会处于阻塞状态):

没有命令去执行载入,在服务器启动时自动载入。

当 Redis 服务器启动时(可见日志), rdbLoad 函数就会被执行, 它读取 RDB 文件, 并将文件中的数据库数据载入到内存中。因此没有特殊指令去执行RDB载入

另外 由于AOF的更新频率比RDB更高,所以当服务器开启了AOF时 服务器会优先加载AOF文件

在这里插入图片描述

在这里插入图片描述
服务器在RDB载入时会处于阻塞状态,直到载入成功。

RDB重点回顾:

在这里插入图片描述

2.AOF(append-only file)持久化 :

AOF 机制对每条写入命令作为日志

在这里插入图片描述

这三条写命令对应的RDB文件为

在这里插入图片描述

在这里插入图片描述

和RDB一样也是启动的时候载入

与快照持久化相⽐,AOF 持久化 的实时性更好,因此已成为主流的持久化⽅案。默认情况下Redis 没有开启 AOF(append only file)⽅式的持久化,可以通过 appendonly 参数开启:

appendonly yes

开启 AOF 持久化后每执⾏⼀条会更改 Redis 中的数据的命令,Redis 就会将该命令写⼊硬盘中
的 AOF ⽂件。AOF ⽂件的保存位置和 RDB ⽂件的位置相同,都是通过 dir 参数设置的,默认的⽂件名是 appendonly.aof。

AOF持久化的步骤:

1.命令追加

2.文件写入

3.文件同步

1.命令追加

当AOF持久化功能打开的时候,服务器没执行完一个写命令之后,会以协议格式被执行将被执行到的写命令追加到服务器状态的aof_buf缓冲区的末尾

在这里插入图片描述

在这里插入图片描述

2.3.文件写入与同步

Redis服务器进程就是一个事件循环,这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复,而时间事件则负责像serverCron函数这样需要定时执行的函数

因为服务器在处理文件事件时,可能会处理写命令,使得一些内容被追加到aof_buf缓冲区里面,所以在服务器每次结束一个事件之前,他都会调用flushAppendOnlyFile()函数,考虑是否将aof_buf的内容写入和保存到AOF文件里面。

伪代码如下:

def eventLoop():
    while True:
        # 处理文件事件,接收命令请求以及发送命令回复
        # 处理命令请求时可能会有新内容被追加到 aof_buf 缓冲区中
        processFileEvents()
        
        # 处理时间事件
        processTimeEvents()
 
        # 考虑是否要将 aof_buf 中的内容写入和保存到 AOF 文件里面
        flushAppendOnlyFile()

1.处理文件事件,接收命令请求以及发送命令回复,处理命令请求时可能会有心得内容被追加到aof_buf缓冲区中

2.处理时间事件

3.考虑是否要将aof_buf的内容写入aof文件里面

flushAppendOnlyFile()函数的行为有服务器配置的选项值appendfsync 来决定

在 Redis 的配置⽂件中存在三种不同的 AOF 持久化⽅式(三种方案都会将aof-buf缓冲区中的内容写入aof文件,只是同步时间介个不同),它们分别是:

appendfsync选项值flushAppendOnlyFile行为效率安全性
always每次有数据修改发⽣时都会将aof_buf中的所有内容写⼊AOF⽂件并同步最慢最安全,即使故障停机,AOF持久化也只会丢失一个事件循环中所产生的命令数据
everysec每次有数据修改发⽣时都会将aof_buf中的所有内容写⼊AOF⽂件,如果上次同步AOF的时间距离现在超过1s,那么再次对AOF文件进行同步,并且这个同步操作是由一个线程专门负责执行的。足够快故障停机,数据库也只丢失一秒钟的命令数据
no每次有数据修改发⽣时都会将aof_buf中的所有内容写⼊AOF⽂件,但并不对AOF文件进行同步,让操作系统决定何时进⾏同步最快故障停机时,会丢失上次同步AOF文件之后所有写命令数据

appendfsync always #每次有数据修改发⽣时都会写⼊AOF⽂件,这样会严重降低Redis的速度

appendfsync everysec #每秒钟同步一次

appendfsync no #让操作系统决定何时进⾏同步

为了兼顾数据和写⼊性能,⽤户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步⼀次AOF ⽂件,Redis 性能⼏乎没受到任何影响。⽽且这样即使出现系统崩溃,⽤户最多只会丢失⼀秒之内产⽣的数据。当硬盘忙于执⾏写⼊操作的时候,Redis 还会优雅的放慢⾃⼰的速度以便适应硬盘的最⼤写⼊速度

AOF持久化的效率和安全性

在这里插入图片描述

在这里插入图片描述

AOF文件的载入与数据还原

因为AOF文件包含了重建数据库状态的所有写命令,所以服务器只要读入并重新执行一遍所有保存的写命令即可,就可以还原服务器关闭之前的数据库状态。

Redis读取AOF文件并还原数据库状态的详细步骤如下:

  1. 创建一个不带网络连接的伪客户端(fake client)因为Redis的命令只能在客户端上下文中执行,而载入AOF文件时所使用的命令直接来源于AOF文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行AOF文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样
  2. 从AOF文件中分析并读取出一条写命令
  3. 使用伪客户端执行被读出的写命令
  4. 一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被处理完毕为止

在这里插入图片描述

AOF后台重写

为了解决AOF文件体积膨胀的问题,Redis提供了AOF文件重写功能。

在这里插入图片描述

AOF文件重写是通过读取服务器当前数据库状态来实现的,与现有的AOF文件没任何关系,不会对现有的AOF文件进行任何的读取、分析或者写入操作。

什么是AOF后台重写?

通过读取数据库中状态,而是不依赖当前AOF文件,将多条命令合并成一条记录命令。

在这里插入图片描述

在这里插入图片描述

为了防止服务器因AOF重写而无法处理请求,Redis 将AOF重写程序放到子进程中直行。

Redis使用子进程处理AOF重写的好处:

  • 子进程进行AOF重写期间,服务器进程可以继续处理命令请求
  • 子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性

不过使用子进程依旧有一个问题需要解决,因为子进程进行AOF重写期间,服务器进程还需要继续处理命令请求,而新的命令可能还会修改DB状态,从而使得服务器当前的DB状态与重写后AOF文件中的状态不一致。

如何解决子进程AOF重写期间,服务器数据库状态与重写后的AOF文件数据库状态不一致的情况

为了解决数据不一致的问题,Redis服务器设置了一个AOF重写缓冲区,这个缓存区在服务器创建了子进程之后开始使用,当Redis服务器执行完一个写命令后,它会同时将这个写命令发送给AOF缓存区和AOF重写缓存区

也就是说子进程进行AOF重写时,服务器进程需要执行一下三个工作:

1.执行客户端发送来的命令

2.将执行后的写命令追加到AOF缓冲区

3.将执行后的写命令追加到AOF重写缓冲区

在这里插入图片描述

**当子进程完成重写工作后,向父进程发送一个信号,父进程接收到信号后,调用信号处理函数(**这个过程服务器进程是阻塞的,不能处理新命令请求):

  • AOF重写缓冲区中的所有内容写入新的AOF文件
  • 原子地覆盖现有的AOF文件

通过这样一种机制已经将AOF重写对服务器性能的影响降到最低

AOF重点回顾:

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值