Redis原理之持久化和主从同步

RDB

在运行情况下,Redis 以数据结构的形式将数据维持在内存中,为了让这些数据在 Redis 重启 之后仍然可用,Redis 分别提供了 RDB 和 AOF 两种持久化模式。
在 Redis 运行时,RDB 程序将当前内存中的数据库快照保存到磁盘文件中,在 Redis 重启动 时,RDB 程序可以通过载入 RDB 文件来还原数据库的状态。
RDB 功能最核心的是 rdbSave 和 rdbLoad 两个函数,前者用于生成 RDB 文件到磁盘,而后者则用于将 RDB 文件中的数据重新载入到内存中:
在这里插入图片描述

保存

rdbSave 函数负责将内存中的数据库数据以 RDB 格式保存到磁盘中,如果 RDB 文件已存在,那么新的 RDB 文件将替换已有的 RDB 文件。在保存 RDB 文件期间,主进程会被阻塞,直到保存完成为止。
save 和 bgsave 两个命令都会调用 rdbSave 函数,但它们调用的方式各有不同:
• save 直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。在主进程阻塞期间, 服务器不能处理客户端的任何请求。
在这里插入图片描述

• bgsave 则 fork 出一个子进程,子进程负责调用 rdbSave ,并在保存完成之后向主 进程发送信号,通知保存已完成。因为 rdbSave 在子进程被调用,所以 Redis 服务器在 BGSAVE 执行期间仍然可以继续处理客户端的请求。
在这里插入图片描述

载入

当 Redis 服务器启动时,rdbLoad 函数就会被执行,它读取 RDB 文件,并将文件中的数据库数据载入到内存中。
在载入期间,服务器每载入 1000 个键就处理一次所有已到达的请求,不过只有 PUBLISH 、 SUBSCRIBE 、PSUBSCRIBE 、UNSUBSCRIBE 、PUNSUBSCRIBE 五个命令的请求会被正确地处理, 其他命令一律返回错误。等到载入完成之后,服务器才会开始正常处理所有命令。

RDB的几个优点:

  1. 与 AOF 方式相比,通过 RDB 文件恢复数据比较快。
  2. RDB 文件非常紧凑,适合于数据备份。
  3. 通过 RDB 进行数据备份,由于使用子进程生成,所以对 Redis 服务器性能影响较小。

RDB 的几个缺点:

  • 如果服务器宕机的话,采用 RDB 的方式会造成某个时段内数据的丢失,比如我们设置 10 分钟同步一次或 5 分钟达到 1000 次写入就同步一次,那么如果还没达到触发条件服务器就死机了,那么这个时间段的数据会丢失。
  • 使用 Save 命令会造成服务器阻塞,直到数据同步完成才能接收后续请求。
  • 使用 Bgsave 命令在 Forks 子进程时,如果数据量太大,Forks 的过程也会发生阻塞,另外,Forks 子进程会耗费内存。

AOF

Redis 分别提供了 RDB 和 AOF 两种持久化机制:
• RDB 将数据库的快照(snapshot)以二进制的方式保存到磁盘中。
• AOF 则以协议文本的方式,将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件,以此达到记录数据库状态的目的。
在这里插入图片描述

AOF 命令同步

Redis 将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件,以此达到记录数据库状态的目的,为了方便起见,我们称呼这种记录过程为同步。

命令传播

当一个 Redis 客户端需要执行命令时,它通过网络连接,将协议文本发送给 Redis 服务器。

比如说,要执行命令 SET KEY VALUE ,客户端将向服务器发送文本 “*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n” 。服务器在接到客户端的请求之后,它会根据协议文本的内容,选择适当的命令函数,并将各个参数从字符串文本转换为 Redis 字符串对象(StringObject)。

比如说,针对上面的 SET 命令例子,Redis 将客户端的命令指针指向实现 SET 命令的 setCommand 函数,并创建三个 Redis 字符串对象,分别保存 SET 、KEY 和 VALUE 三个参数(命 令也算作参数)。每当命令函数成功执行之后,命令参数都会被传播到 AOF 程序,以及 REPLICATION 程序

AOF 保存模式

Redis 目前支持三种 AOF 保存模式,它们分别是:

  1. AOF_FSYNC_NO :不保存。
  2. AOF_FSYNC_EVERYSEC :每一秒钟保存一次。
  3. AOF_FSYNC_ALWAYS :每执行一个命令保存一次。

AOF 的优点:

AOF 只是追加日志文件,因此对服务器性能影响较小,速度比 RDB 要快,消耗的内存较少。

AOF 的缺点:

  • AOF 方式生成的日志文件太大,即使通过 AOF 重写,文件体积仍然很大。
  • 恢复数据的速度比 RDB 慢。

重写机制

随着命令的不断写入AOF,文件会越来越大,为了解决这个问题,Redis 引入AOF重写机制压缩文件体积。AOF文件重写是把Redis 进程内的数据转化为写名命令同比到新AOF文件的过程。
重写后的AOF文件为什么可以变小,有如下原因:

  1. 进程内已经超时的数据不再写入文件。
  2. 旧的AOF文件中含有无效命令,如del key1;hdel key2;set a 1;set a 2;等,重写使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令。
  3. 多条写命令可以合并为一个,如:lpush list a ; lpush list b; lpush list c ;等可以转化为:lpush list a b c 。

手动触发重写过程:

127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
127.0.0.1:6379>

主从同步的实现过程

主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。

默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

主从同步的实现

主从同步分为 2 个步骤:同步和命令传播

同步:将从服务器的数据库状态更新成主服务器当前的数据库状态。
命令传播:当主服务器数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的过程。

上面就是主从同步 2 个步骤的作用,下面我打算稍微细说这两个步骤的实现过程。

这里需要提前说明一下:在 Redis 2.8 版本之前,进行主从复制时一定会顺序执行上述两个步骤,而从 2.8 开始则可能只需要执行命令传播即可。在下文也会解释为什么会这样?

  • 同步

从服务器对主服务的同步操作,需要通过 sync 命令来实现,以下是 sync 命令的执行步骤:

  1. 从服务器向主服务器发送 sync 命令

  2. 收到 sync 命令后,主服务器执行 bgsave 命令,用来生成 rdb 文件,并在一个缓冲区中记录从现在开始执行的写命令。

  3. bgsave 执行完成后,将生成的 rdb 文件发送给从服务器,用来给从服务器更新数据

  4. 主服务器再将缓冲区记录的写命令发送给从服务器,从服务器执行完这些写命令后,此时的数据库状态便和主服务器一致了。
    在这里插入图片描述

  • 命令传播

经过同步操作,此时主从的数据库状态其实已经一致了,但这种一致的状态的并不是一成不变的。

在完成同步之后,也许主服务器马上就接受到了新的写命令,执行完该命令后,主从的数据库状态又不一致。

为了再次让主从数据库状态一致,主服务器就需要向从服务器执行命令传播操作 ,即把刚才造成不一致的写命令,发送给从服务器去执行。从服务器执行完成之后,主从数据库状态就又恢复一致了。

  • 优化版同步操作
    在这里插入图片描述
    从节点发送psync [runId] [offset]命令,主节点有如下响应
    FULLRESYNC:第一次连接,进行全量复制
    CONTINUE:进行部分复制
    ERR:不支持psync命令,进行全量复制

  • Redis 主从复制分为以下三种方式:
    当 Master 服务器与 Slave 服务器正常连接时,Master 服务器会发送数据命令流给 Slave 服务器,将自身数据的改变复制到 Slave 服务器。
    当因为各种原因 Master 服务器与 Slave 服务器断开后,Slave 服务器在重新连上 Master 服务器时会尝试重新获取断开后未同步的数据即部分同步,或者称为部分复制。
    如果无法部分同步(比如初次同步),则会请求进行全量同步,这时 Master 服务器会将自己的 RDB 文件发送给 Slave 服务器进行数据同步,并记录同步期间的其他写入,再发送给 Slave 服务器,以达到完全同步的目的,这种方式称为全量复制。

全量复制流程
在这里插入图片描述

  1. 从节点发送psync ? -1命令,因为第一次发送,不知道主节点的runId,所以为?,因为是第一次复制,所以offset = -1。
  2. 主节点发现从节点是第一次复制,变返回FULLRESYNC {runId} {offset},runId是主节点的runId,offset是主节点目前的offset。
  3. 从节点接收主节点信息后,保存到info中。
  4. 主节点在发送FULLRESYNC后,启动bgsave命令,生成RDB文件(数据持久化)。
  5. 主节点发送RDB文件给从节点。到从节点加载数据完成这段期间主节点的写命令放入缓冲区。
  6. 从节点清理自己的数据库数据。从节点加载RDB文件,将数据保存的自己的数据库中。
  7. 主节点发送缓冲区写名利,从节点执行写命令。

部分复制流程
1)部分复制主要是Redis针对全量复制的过高开销做出的一种优化措施,使用psync {runId}{offset}命令实现。当从节点(slave)正在复制主节点(master)时,如果出现网络闪断或者命令丢失等异常情况时,从节点会向主节点要求补发丢失的命令数据,如果主节点的复制积压缓冲区内存将这部分数据则直接发送给从节点,这样就可以保持主从节点复制的一致性。补发的这部分数据一般远远小于全量数据。

2)主从连接中断期间主节点依然响应命令,但因复制连接中断命令无法发送给从节点,不过主节点内部存在的复制积压缓冲区,依然可以保存最近一段时间的写命令数据,默认最大缓存1MB。当从节点网络恢复后,从节点会再次连上主节点。

3)当主从连接恢复后,由于从节点之前保存了自身已复制的偏移量和主节点的运行ID。因此会把它们当做psync参数发送个主节点,要求进行部分复制操作。

4)主节点接到psync命令后首先核对参数runId是否与自身一致,如果一致,说明之前复制的是当前主节点;之后根据参数offset在自身复制积压缓冲区查找,如果偏移量之后的数据存在缓冲区中,则对从节点发送+COUTINUE响应,表示可以进行部分复制。因为缓冲区大小固定,若发生缓存溢出,则要进行全量复制。

5)主节点根据偏移量把复制积压缓冲区里的数据发送给从节点,保证主从复制进入正常状态。

Redis故障处理

若主节点挂掉后,再次重启,runid的值会变。此时从节点的发送psync命令,会提示找不到原runid,则会再进行一次全量复制。为避免这种状况,使用Redis故障转移机制,主节点挂掉后,从节点升级为主节点。如哨兵模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值