Hello World

Redis主从复制

Redis系列深度好文:深入学习Redis

1. 概念

1.1 定义

  在主从复制中,数据库分为两类,一类是主库(master),另一类是同步主库数据的从库(slave)
  主库可以进行读写操作,当写操作导致数据变化时会自动同步到从库。而从库一般是只读的(特定情况也可以写,通过参数slave-read-only指定),并接受来自主库的数据,
  一个主库可拥有多个从库,而一个从库只能有一个主库。

1.2 作用

  数据冗余:实现了数据的热备份,是持久化之外的一种数据冗余方式。
  故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载;尤其是在写少读多的场景下,可以大大提高Redis服务器的并发量。
  高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

2. 配置方式

2.1 客户端命令

  开启主从同步:slaveof  masterIP masterPort

  断开主从同步:slaveof no one,断开后已经保存的数据不变,后续不再接收主节点的同步数据;

2.2 配置文件

  在配置文件中配置  slaveof <masterip> <masterport>;除了IP和端口信息外,还可以配置其他主从信息;

2.3 启动命令

  执行启动命令时就启动主从同步

  redis-server  --slaveof <masterip> <masterport>

3. 实现原理

  slave向master发送 slaveof 命令后开始进行复制,以下为详细步骤:

 3.1 设置主服务器的IP地址和端口

  slaveof  是一个异步的命令。

  对slave执行 slaveof  masterIP  masterPort 后,slave 将 master 的信息保存到服务器状态的 masterhost 属性和 masterport 属性,属性设置完成后立即返回 OK,表示复制指令已经被接收,实际的复制工作在OK返回之后开始执行。

3.2 建立套接字连接

  slaveof 命令执行后,slave 根据命令设置的IP和端口创建套接字连接,连接建立成功后;

  slave  为套接字关联一个处理复制工作的文件事件处理器,负责接收RDB文件以及传播的命令;

  master 为该套接字创建相应的客户端状态,将 slave 看作一个连接到 master 的客户端对待;slave 可以向 master 发送命令请求,master 则向 slave 返回命令回复;

3.3 发送ping命令

  slave 成为 master 的客户端后,先向 master 发送一个 PING 命令,有两个作用;

  • 检查套接字的读写状态是否正常;
  • 检查 master 能否正常处理命令请求;

  salve 发送 ping 命令后将遇到以下三种情况之一:

  • master 返回一个命令回复, slave 不能在规定时间内取出回复的内容。此时表示网络连接状态不佳,master 和 slave 断开连接后重新建立套接字连接;
  • master 返回一个错误。表示 master 暂时不能处理 slave 的命令请求。master 和 slave 断开连接后重新建立套接字连接;
  • slave 收到 pong 回复。表示连接正常,继续执行下一个步骤;

3.4 身份验证

  slave 收到 pong 回复后,下一步决定是否进行身份验证:

  • 如果 slave 设置的 masterauth 选项,则进行身份验证;否则,不进行身份验证;

  若进行身份验证, slave 发送 auth  masterauth属性值  命令;

  身份验证阶段可能会有以下三种情况:

  • master 未设置 requirepass 选项, slave 未设置 masterauth 选项,则继续执行复制工作;
  • master 设置了 requirepass 选项, slave 未设置 masterauth 选项,master 返回一个 NOAUTH 错误;
  • master 未设置 requirepass 选项, slave 设置了 masterauth 选项,则master 返回 no password is set 错误;
  • salve 的 auth 命令发送的密码和 master 设置的 requirepass 相同,复制工作继续执行,否则 返回一个 invalid password 错误;

3.5 发送端口信息

  slave 执行命令 REPLCONF listening-port  <port-number>,向 master 发送自己的监听端口号;

  master 将端口号记录在 slave 对应客户端状态的 slave_listening_port 属性中,唯一作用是在 master 执行 INFO replication 命令时打印出 slave 的端口号;

3.6 同步

  将 slave 的状态更新为和 master 一样的状态;

  这一步 slave 向 master 发送 PSYNC 命令,进行同步操作;

  同步操作之前,只有 slave 是 master 的客户端,执行之后 master 也成为 slave 的客户端;

  • 若 PSYNC 执行完整重同步,master 成为 slave 的客户端后才能将缓冲区的命令发送给 slave 执行;
  • 若 PSYNC 执行部分重同步,master 成为 slave 的客户端后才能向 slave 发送缓冲区的写命令;

  master 和 slave 互为对方的客户端,可以互相发送命令请求,互相进行命令回复。

3.7 命令传播

  将 master 被修改时将变化实时的同步到 slave;

  同步之后,master 进入命令传播阶段,master 将自己的写命令发送给 salve ,slave 接收并执行写命令。

4. 复制功能的实现

  Redis2.8版本以前,同步通过sync命令实现,在主从同步过程中若发生断线重连,则要重新进行全量同步,效率较低;

  sync命令非常消耗资源

  • maser执行bgsave,耗费大量的CPU,内存和IO资源;
  • RDB文件传输耗费网络资源;
  • slave载入RDB文件时发生阻塞;

  为了解决断线重连后复制的低效问题,Redis2.8版本开始使用PSYNC命令代替SYNC命令;

4.1 PSYNC的两种模式

  • 完整重同步:用于初次复制的情况。和sync命令一样,master创建RDB文件,在缓冲区保存之后的写命令,然后发送给slave;
  • 部分重同步:用于断线重连后重复制的情况。master和slave断连后重新建立连接,如果条件允许,master可以只将断连期间的写命令发送给slave,这样也可以完成同步,而且效率很高。

4.2 部分重同步的实现

  部分重同步主要由以下三个部分构成:

  • master和slave的复制偏移量;
  • master的复制积压缓冲区;
  • 服务器的运行ID;

  重连后slave将自己的偏移量发送给master,master就知道slave需要同步哪些数据。至于是完整重同步还是部分重同步则根据slave的复制偏移量和积压缓冲区的关系进行选择;

4.2.1 复制偏移量

  master和slave各自维持一个复制偏移量。根据偏移量即可判断master和slave是否一致。

  master每次想slave传播N个字节,就将自己的复制偏移量+N;

  slave每次收到N个字节,就将自己的复制偏移量+N;

4.2.2 复制积压缓冲区

  复制积压缓冲区是由master维护的一个固定长度的先进先出队列,默认在1MB。用于保存一定数量最新的写命令。

  master将命令传播给slave时,还会将命令写入复制积压缓冲区里面;

  重连后master收到的slave的复制偏移量在复制积压缓冲区中,表明需要同步的数据全部可以再复制积压缓冲区中取到,则进行部分重同步;否则进行完全重同步。

  合理的设置复制积压缓冲区的大小可以有效的利用部分重同步模式;

  大小公式:缓冲区大小 = 断连时间秒数 * 每秒的写命令;

4.2.3 服务器运行ID

  每个Redis服务器都有自己的运行ID,它在服务器启动时生成,由40个随机的16进制字符组成。

  初次复制时,master将自己的运行ID发送给slave并保存;

  master和slave断线重连后,slave将保存的master运行ID发送给当前连接的主服务器。

  发送的ID和当前主服务器ID一致则尝试进行部分重同步,否则进行完整重同步;

4.3 PSYNC命令的实现

  • slave没复制过任何master或者执行过 slaveof no one:slave发送PSYNC ? -1命令,请求完整重同步;
  • slave复制过master:slave发送 PSYNC  <runid> <offset>,master自己判断进行何种同步;
  • master 返回 +FULLRESYNC  <runid> <offset> 回复,则进行完整重同步,slave保存这个 runid,并将该 offset 作为自己的初始化 offset;
  • master 返回 +CONTINUE 回复,则进行部分重同步,slave等待数据即可;
  • master 返回 - ERR,表示master版本低于2.8;则slave发送 SYNC 命令,进行完整重同步;

5. 心跳检测

  命令传播阶段,salve 默认以每秒一次的频率向 master 发送 REPLCONF ACK <replication_offset(slave的当前复制偏移量)>,主要有以下三个作用;

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

5.1 检测主从服务器的网络连接状态

  master 和 slave 通过发送和接收 REPLCONF ACK 命令检测两者之间的网络连接状态;

  maser 超过一秒未接收到 slave 发送的 REPLCONF ACK ,master 就知道连接出现问题了;

  通过向 master  发送 info replication 命令,在 lag 一栏中可以看到 slave 最后一次发送 REPLCONF ACK 命令距离现在过了多少秒了;

5.2 辅助实现 min-slaves 选项

  Redis 的 min-slaves-to-write 和 min-slaves-max-lag 选项可以防止 master 在不安全的情况下执行写命令;

  min-slaves-to-write  3

  min-slaves-max-lag 10

  salve 数量属于3个,或者3个slave 的延迟值都大于等于10秒,master 拒绝执行写命令;

5.3 检测命令丢失

  若因为网络故障导致 master 发送的 写命令丢失,则 slave 发送 REPLCONF ACK 时,master 发现复制偏移量不一致,则将复制积压缓冲区的缺失命令补发给 slave;

  这和部分重同步不一样,命令补发在没有断线的情况下进行的,部分重同步在断线重连后进行。

posted @ 2018-10-26 20:49  小小忧愁米粒大  阅读(190)  评论(0编辑  收藏  举报
瞅啥瞅,好好看书