Redis主从同步

主从同步

  1. Redis采用读写分离的方式,首先封住数据来源只有一个。 (一致性)
  • 第一次同步
    1. slave 通过命令replicaof 127.0.0.1 6379与master建立联系
    2. 链接建立后slave会发送psync ? -1获取拉取数据范围(?: 第一次链接不知道master的runId(每台服务启动时生成的随机id) -1: 标识全量复制)
    3. master接收到psync消息后回应+FULLRESYNC runID, offset 标识此次是全量复制
    4. master fock子进程生成RDB 发送给slave
    5. slave收到RDB文件后清除内存数据后同步RDB

master fock子进程后 产生的数据更新都会被缓存在 slave的buffer(replacetion buffer)中,同步给slave来保证数据同步。

  • 数据同步
    master每执行一个更新命令就会往replacetion buffer插入一条,并通过与slave建立的长链接(避免频繁建立链接的开销)发送到slave

  • 长链接断开后从链接怎么办: repl_backlog_buffer就来了
    master每执行一个更新命令再给repl_backlog_buffer中插入一条用作备份,同时master维护master_repl_offset,slave维护slave_repl_offset
    从链后slave发送偏移量到master,master计算根当前进度差了多少,然后将差了的写入到replcation buffer发送到slave。
    这里需要注意repl_backlog_buffer到设计是环形的(redo log一样),所以slave停机太久可能造成数据已被覆盖,这个时候就会促发(第一次同步)的方案啦。
    repl_backlog_buffer大小调整repl_buffer_size

  • repl_backlog_buffer:它是为了从库断开之后,如何找到主从差异数据而设计的环形缓冲区,从而避免全量同步带来的性能开销。如果从库断开时间太久,repl_backlog_buffer环形缓冲区被主库的写命令覆盖了,那么从库连上主库后只能进行一次全量同步,所以repl_backlog_buffer配置尽量大一些,可以降低主从断开后全量同步的概率。

  • replication buffer:Redis和客户端通信也好,和从库通信也好,Redis都需要给分配一个 内存buffer进行数据交互,客户端是一个client,从库也是一个client,我们每个client连上Redis后,Redis都会分配一个client buffer,所有数据交互都是通过这个buffer进行的:Redis先把数据写到这个buffer中,然后再把buffer中的数据发到client socket中再通过网络发送出去,这样就完成了数据交互。所以主从在增量同步时,从库作为一个client,也会分配一个buffer,只不过这个buffer专门用来传播用户的写命令到从库,保证主从数据一致,我们通常把它叫做replication buffer。

    • 如果主从在传播命令时,因为某些原因从库处理得非常慢,那么主库上的这个buffer就会持续增长,消耗大量的内存资源,甚至OOM。所以Redis提供了client-output-buffer-limit参数限制这个buffer的大小,如果超过限制,主库会强制断开这个client的连接,也就是说从库处理慢导致主库内存buffer的积压达到限制后,主库会强制断开从库的连接,此时主从复制会中断,中断后如果从库再次发起复制请求,那么此时可能会导致恶性循环,引发复制风暴,这种情况需要格外注意。

哨兵

解决分布式系统下master节点不可用。

  • 哨兵怎么实现的?

    1. 基于Redis的 pub/sub
    2. 当执行sentinel monitor <master-name> <ip> <port> <quorum>启动哨兵时会执行SUBSCRIBE __sentinel__:hello,同时将自己的地址发布上去
    3. 再有哨兵加入集群时之前订阅的哨兵节点会收到master节点推来的新节点加入消息。从而建立链接
  • 哨兵如何知道从库地址?

  • 当发生master节点不可用由哪个哨兵节点执行替换流程?

    1. 当发现master在down-after-milliseconds时长内不可用时会向其他节点发送is-master-down-by-addr命令,超过(配制的)quorum赞成后进入Leader选举出执行操作的节点。
    2. 每个节点都可能得到 “超过(配制的)quorum赞成”,所以每个节点都可能发出leader选举操作。
      (发起leader选举的节点那一票投给自己 )
      1. 假设3个节点a,b,c。 节点a,b同时发起leader选举,假设c先收到a的投票请求,那么c就会给a投一票,后续收到的请求就都反对。 节点a在得到票数大于半数节点且大于quorum数后将成为leader节点。
      2. 假设3个节点a,b,c。 节点a,b, c同时发起leader选举, 那么三个节点都达不到成为leader的条件,集群会等待(哨兵故障转移超时时间的 2 倍)后超时,重新发起新一轮选举。
        (leader选举也是很依赖网络环境的,假设有一台网络环境不好,也可能导致整个集群达不到成为leader的必要条件)
        可以适当调大down-after-milliseconds避免误判
  • 客户端可以查到当前主从替换走到哪个流程了么?
    哨兵其实也是运行的Redis,所以也可以通过pub/sub方案获取进度


哨兵会PING集群下的节点,发现节点在down-after-milliseconds时长内不可用标记为“主观不可用”,如果标记的节点是master节点。就开始启用主从替换流程。这里为了确定master是真的不可用了,哨兵服务可能会存在多个,这个时候哨兵就会联系集群中其他实例,如果 认为master不可用的实例数大于 实例数/2+1,就会标记为客观不可用(认为master节点不可用)。

  • 主从替换流程
    主从替换期间系统写功能将停止使用;
  1. 排除
    排除历史不可用次数大于down-after-milliseconds次的;
  2. 打分
    slave-priority优先级最高的从库直接成为master
    slave_repl_offset最接近master_repl_offset的打分最高
    实例ID最小的优先
  • 选出新的master节点后通知从库和客户端
    • 通知从库: 执行replicaof命令指向新的master节点
    • 通知客户端:
      1. 哨兵会将master几点的新地址写入自己pubsub中,客户端通过订阅这个pubsub可以感知到master节点发生变化近而更换操作地址。
      2. 也可以通过sentinel get-master-addr-by-name主动拉取最新的master地址
posted @ 2021-12-16 16:44  ccme  阅读(162)  评论(0)    收藏  举报