Redis(2)--持久化,主从同步

Redis的持久化机制

可以参考这篇文章Redis提供的持久化机制 

在讲解Redis的持久化机制之前,我们首先得明白,到底什么是持久化呢?

简单来讲就是将数据放到断电后数据不会丢失的设备中,也就是我们通常理解的硬盘上。

Redis提供了RDB持久化和AOF持久化

RDB持久化

RDB持久化是默认的持久化方式,在指定的时间间隔内将内存中的数据集快照写入磁盘。其实就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。可以通过配置设置自动做快照持久化的方式。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置。

save 900 1     #900秒内如果超过1个key被修改,则发起快照保存
save 300 10    #300秒内容如超过10个key被修改,则发起快照保存
save 60 10000

redis在进行数据持久化的过程中,会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件。正是这种特性,让我们可以随时来进行备份,因为快照文件总是完整可用的。

对于RDB方式,redis会单独创建(fork)一个子进程来进行持久化,而主进程是不会进行任何IO操作的,这样就确保了redis极高的性能。

优点
  • 采用该方式,整个Redis数据库将只包含一个文件,容易进行备份。
  • 方便备份,我们可以很容易的将一个一个RDB文件移动到其他的存储介质上。
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
  • RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork
    出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。
缺点
  • 若尽量避免在服务器故障时丢失数据,那么RDB方式不适合。虽然 Redis 允许你设置不同的保存点(save point)来控制保存 RDB文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。
  • 每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时,fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF重写的执行间隔有多长,数据的耐久性都不会有任何损失。
AOF持久化

AOF,即Append Only File,只允许追加不允许改写的文件。其实AOF方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍。

默认的AOF持久化策略是每秒钟fsync一次(fsync是指把缓存中的写指令记录到磁盘中),因为在这种情况下,redis仍然可以保持很好的处理性能,即使redis故障,也只会丢失最近1秒钟的数据。

因为采用了追加方式,如果不做任何处理的话,AOF文件会变得越来越大,为此,redis提供了AOF文件重写(rewrite)机制,即当AOF文件的大小超过所设定的阈值时,redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。

在进行AOF重写时,仍然是采用先写临时文件,全部完成后再替换的流程,所以断电、磁盘满等问题都不会影响AOF文件的可用性。

优点
  • 使用 AOF 持久化会让 Redis 变得非常耐久(much more durable):你可以设置不同的 fsync 策略,比如无fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。 AOF 的默认策略为每秒钟 fsync一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据。
  • AOF 文件是一个只进行追加操作的日志文件(append only log), 因此对 AOF 文件的写入不需要进行 seek ,即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等), redis-check-aof工具也可以轻易地修复这种问题。 Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。
缺点
  • 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
  • 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB可以提供更有保证的最大延迟时间(latency)。
  • AOF 在过去曾经发生过这样的 bug : 因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。(举个例子,阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug 。) 测试套件里为这种情况添加了测试:它们会自动生成随机的、复杂的数据集, 并通过重新载入这些数据来确保一切正常。 虽然这种 bug 在 AOF 文件中并不常见,但是对比来说, RDB 几乎是不可能出现这种 bug 的。
如何选择RDB和AOF
  • 如果想得到更可靠的持久化方案, 你应该同时使用两种持久化功能。
  • 如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。

Redis的主从同步

关于Redis的主从同步,这篇文章介绍得非常好,下面是对该文章的引用 redis学习笔记——主从同步(复制)

我们先介绍Redis2.8之前的主从同步实现机制,分析它的不足,然后我们再详细讲解Redis2.8之后的主从同步机制。

Redis2.8之前的主从同步实现机制

Redis的复制功能分为同步(sync)和命令传播两个操作

  • 同步:将从服务器的数据库状态更新至主服务器当前所处的数据库状态
  • 命令传播:在主服务器的数据库状态被修改,导致主从服务器的数据库状态出现不一致时,让主从服务器的数据库重新回到一致状态
同步

当客户端向从服务器要求从服务器复制主服务器时,从服务器首先需要执行同步操作,也就是,将从服务器的数据库状态更新至主服务器当前所处的数据库状态。

从服务器对主服务器的同步操作需要通过向主服务器发送SYNC命令来完成

  1. 从服务器向主服务器发送SYNC命令;
  2. 收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令;
  3. 当主服务器的BGSAVE命令执行完毕时,主服务器会将BGSAVE命令生成的RDB文件发送给从服务器,从服务器接收并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态。
  4. 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。
命令传播

在执行完同步操作之后,主从服务器之间数据库状态已经相同了。但这个状态并非一成不变,如果主服务器执行了写操作,那么主服务器的数据库状态就会修改,并导致主从服务器状态不再一致。

所以为了让主从服务器再次回到一致状态,主服务器需要对从服务器执行命令传播操作:主服务器会将自己执行的写命令,也即是造成主从服务器不一致的那条写命令,发送给从服务器执行,当从服务器执行了相同的写命令之后,主从服务器将再次回到一致状态。

Redis2.8之前的主从同步实现机制的缺陷

看起来这个机制很完美,但其实正如世上并无十全十美的事件一样,该机制也有它存在的一些问题。

我们知道,在Redis中,从服务器对主服务器的复制可以分为以下两种情况:

  • 初次复制:从服务器以前没有复制过任何主服务器,或者从服务器当前要复制的主服务器和上一次复制的主服务器不同;
  • 断线后重复制:处于命令传播阶段的主从服务器因为网络原因而中断了复制,但从服务器通过自动重连接重新连上了主服务器,并继续复制主服务器。

对于初次复制来说,旧版复制功能能够很好地完成任务,但对于断线后重复制来说,旧版复制功能虽然也能让主从服务器重新回到一致状态,但效率却非常低。

何以见得其低效

主从服务器断开的时间越短,主服务器在断线期间执行的写命令就越少,而执行少量写命令所产生的数据量通常比整个数据库的数据量要少得多,在这种情况下,为了让从服务器补足一小部分缺失的数据,却要让主从服务器重新执行一次SYNC命令,这种做法无疑是非常低效的。

SYNC是一个很耗费资源的操作

SYNC命令是非常消耗资源的,因为每次执行SYNC命令,主从服务器需要执行以下操作:

  1. 主服务器需要执行BGSAVE命令来生成RDB文件,这个生成操作会耗费主服务器大量的CPU、内存和磁盘I/O资源;
  2. 主服务器需要将自己生成的RDB文件发送给从服务器,这个发送操作会耗费主从服务器大量的网络资源(带宽和流量),并对主服务器响应命令请求的时间产生影响;
  3. 接收到RDB文件的从服务器需要载入主服务器发来的RDB文件,并且在载入期间,从服务器会因为阻塞而没办法处理命令请求。

SYNC是一个如此消耗资源的命令,所以Redis最好在真需要的时候才需要执行SYNC命令。

Redis2.8及以后的主从同步实现机制

为了解决旧版复制功能在处理断线重复制情况时的低效问题,Redis从2.8版本开始,使用PSYNC命令代替SYNC命令来执行复制时的同步操作。

PSYNC命令具有完整重同步(full resynchronization)和部分重同步(partial resynchronization)两种模式:

  • 其中完整重同步用于处理初次复制情况:完整重同步的执行步骤和SYNC命令的执行步骤基本一样,它们都是通过让主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区里面的写命令来进行同步;
  • 而部分重同步则用于处理断线后重复制情况:当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可以将数据库更新至主服务器当前所处的状态。
部分重同步是如何实现的

部分重同步功能由以下三个部分构成:

  • 主服务器的复制偏移量(replication offset)和从服务器的复制偏移量
  • 主服务器的复制积压缓冲区(replication backlog)
  • 服务器的运行ID(run ID)
复制偏移量

执行复制的双方——主服务器和从服务器会分别维护一个复制偏移量:

  • 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N;
  • 从服务器每次收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量的值加上N;

通过对比主从服务器的复制偏移量,程序可以很容易地知道主从服务器是否处于一致状态:如果主从服务器处于一致状态,那么主从服务器两者的偏移量总是相同的;相反,如果主从服务器两者的偏移量并不相同,那么说明主从服务器并未处于一致状态。

复制积压缓冲区

复制积压缓冲区是由主服务器维护的一个固定长度(fixed-size)先进先出(FIFO)队列,默认大小为1MB。

和普通先进先出队列随着元素的增加和减少而动态调整长度不同,固定长度先进先出队列的长度是固定的,当入队元素的数量大于队列长度时,最先入队的元素会被弹出,而新元素会被放入队列。

当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区里面。
在这里插入图片描述
因此,主服务器的复制积压缓冲区里面会保存着一部分最近传播的写命令,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量。

当从服务器重新连上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器,主服务器会根据这个复制偏移量来决定对从服务器执行何种同步操作:

  • 如果offset偏移量之后的数据(也即是偏移量offset+1开始的数据)仍然存在于复制积压缓冲区里面,那么主服务器将对从服务器执行部分重同步操作;
  • 相反,如果offset偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作。
服务器运行ID

除了复制偏移量和复制积压缓冲区之外,实现部分重同步还需要用到服务器运行ID(run ID):

  • 每个Redis服务器,不论主服务器还是从服务,都会有自己的运行ID;
  • 运行ID在服务器启动时自动生成,由40个随机的十六进制字符组成,例如53b9b28df8042fdc9ab5e3fcbbbabff1d5dce2b3;

当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来(注意哦,是从服务器保存了主服务器的ID)。

当从服务器断线并重新连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的运行ID:

  • 如果从服务器保存的运行ID和当前连接的主服务器的运行ID相同,那么说明从服务器断线之前复制的就是当前连接的这个主服务器,主服务器可以继续尝试执行部分重同步操作;
  • 相反地,如果从服务器保存的运行ID和当前连接的主服务器的运行ID并不相同,那么说明从服务器断线之前复制的主服务器并不是当前连接的这个主服务器,主服务器将对从服务器执行完整重同步操作。
PSYNC命令的实现

PSYNC命令的调用方法有两种:

  1. 如果从服务器以前没有复制过任何主服务器,或者之前执行过SLAVEOF no one命令,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC ? -1命令,主动请求主服务器进行完整重同步(因为这时不可能执行部分重同步);
  2. 相反地,如果从服务器已经复制过某个主服务器,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC
    命令:其中runid是上一次复制的主服务器的运行ID,而offset则是从服务器当前的复制偏移量,接收到这个命令的主服务器会通过这两个参数来判断应该对从服务器执行哪种同步操作。

根据情况,接收到PSYNC命令的主服务器会向从服务器返回以下三种回复的其中一种:

  1. 如果主服务器返回+FULLRESYNC <runid><offset>回复,那么表示主服务器将与从服务器执行完整重同步操作:其中runid是这个主服务器的运行ID,从服务器会将这个ID保存起来,在下一次发送PSYNC命令时使用;而offset则是主服务器当前的复制偏移量,从服务器会将这个值作为自己的初始化偏移量;
  2. 如果主服务器返回+CONTINUE回复,那么表示主服务器将与从服务器执行部分重同步操作,从服务器只要等着主服务器将自己缺少的那部分数据发送过来就可以了;
  3. 如果主服务器返回-ERR回复,那么表示主服务器的版本低于Redis2.8,它识别不了PSYNC命令,从服务器将向主服务器发送SYNC命令,并与主服务器执行完整同步操作。
心跳检测

在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令:REPLCONF ACK <replication_offset>

其中replication_offset是从服务器当前的复制偏移量。

发送REPLCONF ACK命令对于主从服务器有三个作用:

  1. 检测主从服务器的网络连接状态;
  2. 辅助实现min-slaves选项;
  3. 检测命令丢失。
检测主从服务器的网络连接状态

如果主服务器超过一秒钟没有收到从服务器发来的REPLCONF ACK命令,那么主服务器就知道主从服务器之间的连接出现问题了。

辅助实现min-slaves配置选项

Redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令。

举个例子,如果我们向主服务器提供以下设置:

min-slaves-to-write 3
min-slaves-max-lag 10

那么在从服务器的数量少于3个,或者三个从服务器的延迟(lag)值都大于或等于10秒时,主服务器将拒绝执行写命令,这里的延迟值就是上面提到的INFO replication命令的lag值。

检测命令丢失

我们从命令:REPLCONF ACK <replication_offset>就可以知道,每发送一次这个命令从服务器都会向主服务器报告一次自己的复制偏移量。那此时尽管主服务器发送给从服务器的SET key value丢失了。也无所谓,主服务器马上就知道了。

参考;Redis提供的持久化机制 
redis学习笔记——主从同步(复制)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值