Redis的持久化-Redis(三)

目录

1、RDB快照

1.1RDB的文件载入

1.2写时复制(COW)机制

1.3RDB可以频繁的进行快照操作吗?

2、AOF(append-only file)

2.1、命令执行后写日志

2.2、AOF缓冲区

2.3、三种写磁盘策略

2.4、AOF重写

2.5、AOF重写过程

3、RDB和AOF的选择

4、Redis4.0混合持久化     

5、如何选择持久化方式




        Redis的专栏前两篇已经讲述了基础和应用场景,这篇文章我们来说说Redis的持久化。Redis的持久化有两种方式,分别是RDB(Redis DataBase)和AOF(Append Only File),Redis默认采用的是RDB方式。

1、RDB快照

        在默认情况下,Redis将内存数据库快照保存到名字为dump.rdb的二进制文件中。你可以配置持久化的策略:save N M(redis在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次 数据集)。

redis的策略例子:

save 900 1 -- 900秒内至少有一个改动

save 300 10 -- 300秒内至少有10个改动

save 60 10000 -- 60秒内至少有10000个改动

注:如果要关闭RDB,只要将所有的save保存策略注释掉即可,例如 #save 60 10000

除了RDB的自动保存策略,还可以手动执行命令,生成RDB快照,在redis的客户端执行save或bgsave可以生成dump.rdb文件,每次执行命令都会将redis内存数据快照到一个临时的rdb文件里,等到持久化结束就会覆盖原有的rdb快照文件。

1.1RDB的文件载入

        在Redis启动的时候,由于之前的数据时保存在内存中,所以启动的一瞬间,Redis在内存中是没有数据的,如果AOF持久化功能是关闭的,那么在检测到RDB文件后,Redis就会自动加载RDB文件。

需要注意两点:

(1)因为AOF文件更新频率高于RDB文件,保存的数据比RDB文件完成,所以如果开启了AOF持久化,Redis会优先使用AOF文件来还原数据。所以只有在AOF持久化关闭的状态下,Redis才会使用RDB文件来还原数据。

(2)Redis在载入RDB文件期间,会一直处于阻塞状态,直到数据载入完成为止。

1.2写时复制(COW)机制

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

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

1.3RDB可以频繁的进行快照操作吗?

        通过上述讲解我们了解到,RDB在恢复数据的时候会丢失一部分未写入到磁盘的数据,那么我们是不是可以缩短快照时间间隔,因为间隔时间越小,丢失的数据越少,那么我们是否可以这样做呢?

        答案是否定的,虽然bgsave执行时不占用主线程,但是这并不代表可以频繁的执行快照操作。

        首先,持久化是写入磁盘的一个操作,频繁的写入磁盘,会给磁盘带来较大的压力。频繁的快照容易造成前一个快照没有写完后一个快照又开始了,这样多个快照争抢磁盘带宽,造成恶性的循环。

        其次,bgsave执行时虽然不会阻塞主线程,但是fork子进程是由主进行来完成的,会阻塞主进程,fork子进程需要拷贝进程必要的数据结构,其中有一项就是拷贝内存页表(虚拟内存和物理内存的映射索引表),这个拷贝会消耗大量的CPU,拷贝完成之前会阻塞主进程,阻塞的时间取决于整个实例的内存大小,实例越大,内存页表越大,消耗时间越多,fork阻塞的时间也就越多;所以如果频繁的fork子进程是会对主进程有影响的。

        那么有人可能会想到,是否可以做增量的快照呢?也就是下一次快照只是针对上一次快照修改、删除、增加的部分做快照。首先这个思路本身是可以的,但是增量快照需要记录哪些数据有改动,这样无疑需要增加额外的元数据来记录这些信息,对于本来就资源紧张的内存来说这并不是一个很好的方案。

        那综上所述,不能进行频繁的快照,那么如何解决数据丢失的问题呢?那么就引入了Redis提供的另外一个持久化工具-AOF(append to file)。

2、AOFappend-only file)

        AOF持久化是通过保存Redis执行写命令来记录数据库状态的。即每执行一个命令,就把该命令写到日志文件里,该文件是appendonly.aof。

2.1、命令执行后写日志

        AOF的写日志是在每次Redis写命令执行后才执行,如下图:


这样做的好处是,首先不会阻塞当前的命令执行;其次可以避免错误的命令写入日志中,如果当前执行的Redis命令有误,那么在执行命令语法的时候就会检测到,不会再写入到aof日志文件中,进而也避免了在数据恢复时碰到错误命令。

但是也仍然存在两种问题:

(1)AOF 虽然避免了对当前命令的阻塞,但却可能会给下一个操作带来阻塞风险。因为,AOF 日志是在主进程中执行的,如果在把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而导致后续的操作也无法执行了

(2)如果刚执行完一个命令,还没有来得及记日志就宕机了,那么这个命令和相应的数据就有丢失的风险。如果此时 Redis 是用作缓存,还可以从后端数据库重新读入数据进行恢复,但是,如果 Redis 是直接用作数据库的话,此时,因为命令没有记入日志,所以就无法用日志进行恢复了。

2.2、AOF缓冲区

        针对上面两个问题,Redis提供了缓冲区来解决。达到避免阻塞和数据丢失的问题。

        Redis在执行完命令进行持久化的时候,并不是直接写入到磁盘的日志文件中,而是先写入AOF缓冲区内,之后再通过某种策略写到磁盘。如下图:

2.3、三种写磁盘策略

         Redis AOF提供了三种写磁盘的策略。

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

 Redis默认的AOF策略是appendfsync everysec(每秒 fsync 一次),这种 fsync 策略可以兼顾速度和安全性。

        选择了合适的回写策略,AOF持久化还有其他问题吗?AOF文件过大就是主要问题了,AOF持久化是通过保存被执行的命令,随时时间的流逝,AOF文件中的内容会越来越多,过大的AOF文件会使得追加命令变慢,而且对Redis服务器、宿主机有影响。AOF文件过大,对于使用AOF文件来进行数据还原所需的时间也相应增加。那么如何缓解这个问题呢,AOF重写机制可以做到。

2.4、AOF重写

         AOF文件是追加的方式逐一记录命令的。当一个键值对被写命令多次修改,AOF文件中就会记录多条相对应的命令。如下图所示:

         实际上只要将readcount设置为5这一条命令就可以了,所以AOF重写后,会将多条命令变成一条命令,这样既可以节省磁盘空间,减小AOF文件大小,而且在通过AOF数据还原时节省时间。下图为重写后的命令保存格式:

2.5、AOF重写过程

        AOF重写,会使得文件变小,但是把整个数据库的数据写回磁盘,仍然是一件很耗时的事情,那么这个过程是否会导致主线程阻塞呢?下面说下AOF的重写过程。

        Redis的命令执行时单线程执行的,并且有前面RDB的bgsave的讲解,我们会想到AOF的重写不会直接在主线程执行,而是fork一个子进程来执行。所以Redis会fork一个bgrewriteaof的紫禁城来完成;使用子进程可以避免使用锁的情况,保证数据的安全性,但是如何保证数据完整性和一致性呢?

例如在重写开始后主线程又接收新的命令对键值对进行修改,那么子进程是无法感知到的,这样会导致子进程重写完成后数据和原始数据库不一致的问题。

如下图:

 在T6时刻服务器进程有了4个键,而子进程却只有1个键。

为了解决重写时子进程数据不一致问题,Redis设置了一个AOF重写缓冲区。如下图:

在子进程执行AOF重写期间。服务器进程需要执行以下3个动作:

  1. 执行客户端命令

  2. 执行后追加到AOF缓冲区

  3. 执行后追加到AOF重写缓冲区

子进程完成AOF重写后,它向父进程发送一个信号,父进程收到信号后会调用一个信号处理函数,该函数把AOF重写缓冲区的命令追加到新AOF文件中然后替换掉现有AOF文件。父进程处理完毕后可以继续接受客户端命令调用,可以看出在AOF后台重写过程中只有这个信号处理函数会阻塞服务器进程。
下表是完整的AOF后台重写过程:

 

 这样就可以保证重写日志期间的所有操作也都会写入新的AOF文件。

需要注意的是, T7 T8执行的任务会阻塞服务器处理命令。

3、RDB和AOF的选择

RDB 和 AOF ,我应该用哪一个?

在生产环境中,两种我们可以都启用,在Redis启动时如果有aof文件会优先选用aof文件进行数据的恢复,因为一般来说aof数据更全一些。 

4、Redis4.0混合持久化     

        重启 Redis 时,我们很少使用 RDB来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重 放,但是重放 AOF 日志性能相对 RDB来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很 长的时间。 Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。 通过如下配置可以开启混合持久化(必须先开启aof ):
aof use rdb preamble yes
        
        如果开启了混合持久化,AOF在重写时 ,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将 重写这一刻之前 的内存做RDB快照处理,并且将RDB快照内容和 增量的 AOF修改内存数据的命令存在一 起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改 名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。
        于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,因此重启效率大幅得到提升。

5、如何选择持久化方式

        在技术方案选择中,我们需要平衡与取舍找到适合自己的方案。下面提供持久化方案选择的方法:

  • 如果你的业务场景需要很高的性能,或者宕机之后能够尽快的恢复,而对数据完整性的要求不是那么高,那么可以采用RDB持久化的方式。

  • 如果你的业务场景对数据完整性的要求很高,那么可以采用AOF的持久化方式,而至于采用那种回写策略,则取决于你对数据完整性的要求程度。

  • 如果你的业务场景既要兼顾性能,又注重数据完整性,那么可以采用混合持久化的方式。

  • 如果你对数据丢失无所谓,追求性能最大化的情况下,甚至可以禁用持久化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牧之-猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值