zoukankan      html  css  js  c++  java
  • 【转载】redis的主从复制

    主从复制

    放一篇主从复制原理讲的非常好的文章链接 https://www.cnblogs.com/kismetv/p/9236731.html 下面的很多都是从这篇文章复制过来的,因为实在是无可挑剔

    简介

    通俗的讲,主从复制就是主服务器数据更新之后,根据配置和策略,将主服务器的数据自动同步到从服务器,也叫master/slave 机制 ,master以写为主,slave以读为主。可以起到读写分离,容灾恢复数据的功能。还有一个重要的功能就是负载均衡,配合读写分离,尤其是读多写少的时候,配置多个从节点,多个节点一起可以有效的降低服务器的压力

    用法

    所有的主从复制的开启,都是在从节点上开启,不需要动子节点,开启主从复制有三种方式,

    1. 启动从服务器redis客户端后,执行slaveof 主机ip 主机端口
    2. 从节点配置文件加入 slaveof 主机ip 主机端口
    3. redis-server启动时在后面加入 --slaveof 主机ip 主机端口

    以上三种方式都可以成为启动主从复制,可以使用info replication查看主从情况 ,启动之后,从机会自动的在主机上同步所有的数据, 然后可以在主机进行写操作,例如set k1 V1,主机的会自动同步到从机上.

    注意:一旦成为从机 将不能执行和主机写命令,从机只能读,不允许写,否则会报错

    (error) READONLY You can't write against a read only slave.
    

    2.当主机挂了之后,从机还是从机,原地待命,不会成为主机,当主机回来了之后,一切照旧,当从机挂了,与master断开连接,就需要重新连接master,而不会自动再成为从机 ,当然可以在配置文件中配置,断开连接后自动重新连接主机。事实上在redis集群中,当主机挂了之后,集群剩余的机器会投票一台从机成为新的主机。在这种情况下,当原来的主机重启之后,他就不能重新自动变回主机了

    主从复制原理

    主从复制过程大体可以分为3个阶段:连接建立阶段(即准备阶段)、数据同步阶段、命令传播阶段;下面分别进行介绍。

    一)连接建立阶段

    1. 保存主节点信息

    当执行了slaveof 命令后,从节点会存储主节点的ip和端口号的信息,

    1. 建立socket连接

      然后根据这ip和端口号建立socket连接,同时,从节点将为这个socket建立一个专门用于复制文件的处理程序,而主节点接到从节点的socket连接之后, 将从节点看做是连接到主节点的一个客户端,后面的步骤会以从节点向主节点发送命令请求的形式来进行。 也会为这个socket建立相关的客户端程序。

    2. 发送ping命令
      从节点成为主节点的客户端之后,发送ping命令进行首次请求,目的是:检查socket连接是否可用,以及主节点当前是否能够处理请求。

      从节点发送ping命令后,可能出现3种情况:

      (1)返回pong:说明socket连接正常,且主节点当前可以处理请求,复制过程继续。

      (2)超时:一定时间后从节点仍未收到主节点的回复,说明socket连接不可用,则从节点断开socket连接,并重连。

      (3)返回pong以外的结果:如果主节点返回其他结果,如正在处理超时运行的脚本,说明主节点当前无法处理命令,则从节点断开socket连接,并重连。

    二.数据同步阶段

    主从节点之间的连接建立以后,便可以开始进行数据同步,该阶段可以理解为从节点数据的初始化。具体执行的方式是:从节点向主节点发送psync命令(Redis2.8以前是sync命令),开始同步。

    数据同步阶段是主从复制最核心的阶段,根据主从节点当前状态的不同,可以分为全量复制和部分复制,下面会有一章专门讲解这两种复制方式以及psync命令的执行过程,这里不再详述。

    需要注意的是,在数据同步阶段之前,从节点是主节点的客户端,主节点不是从节点的客户端;而到了这一阶段及以后,主从节点互为客户端。原因在于:在此之前,主节点只需要响应从节点的请求即可,不需要主动发请求,而在数据同步阶段和后面的命令传播阶段,主节点需要主动向从节点发送请求(如推送缓冲区中的写命令),才能完成复制。

    三. 命令传播阶段

    数据同步阶段完成后,主从节点进入命令传播阶段;在这个阶段主节点将自己执行的写命令发送给从节点,从节点接收命令并执行,从而保证主从节点数据的一致性。

    在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:PING和REPLCONF ACK。从节点会向主节点定时的发送ping命令来检测主节点的超时情况,

    全量复制

    全量复制就是把主节点所有的数据都复制过来,增量复制就是在全量复制之后,主节点增加多少数据,从节点就只复制主节点增加的数据。全量复制一般是在初次复制阶段,在第一次全量复制之后,往后的复制都是增量复制。

    Redis通过psync命令进行全量复制的过程如下:

    (1)从节点判断无法进行部分复制,向主节点发送全量复制的请求;或从节点发送部分复制的请求,但主节点判断无法进行全量复制;具体判断过程需要在讲述了部分复制原理后再介绍。

    (2)主节点收到全量复制的命令后,执行bgsave,在后台生成RDB文件,并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有写命令

    (3)主节点的bgsave执行完成后,将RDB文件发送给从节点;从节点首先清除自己的旧数据,然后载入接收的 RDB 文件 ,将数据库状态更新至主节点执行bgsave时的数据库状态

    (4)主节点将前述复制缓冲区中的所有写命令发送给从节点,从节点执行这些写命令,将数据库状态更新至主节点的最新状态

    (5)如果从节点开启了AOF,则会触发bgrewriteaof的执行,从而保证AOF文件更新至主节点的最新状态

    通过全量复制的过程可以看出,全量复制是非常重型的操作:

    (1)主节点通过bgsave命令fork子进程进行RDB持久化,该过程是非常消耗CPU、内存(页表复制)、硬盘IO的;关于bgsave的性能问题,可以参考 深入学习Redis(2):持久化

    (2)主节点通过网络将RDB文件发送给从节点,对主从节点的带宽都会带来很大的消耗

    (3)从节点清空老数据、载入新RDB文件的过程是阻塞的,无法响应客户端的命令;如果从节点执行bgrewriteaof,也会带来额外的消耗

    增量复制

    由于全量复制在主节点数据量较大时效率太低,因此Redis2.8开始提供部分复制,用于处理网络中断时的数据同步。

    部分复制的实现,依赖于三个重要的概念:

    (1)复制偏移量

    主节点和从节点分别维护一个复制偏移量(offset),代表的是主节点向从节点传递的字节数;主节点每次向从节点传播N个字节数据时,主节点的offset增加N;从节点每次收到主节点传来的N个字节数据时,从节点的offset增加N。

    offset用于判断主从节点的数据库状态是否一致:如果二者offset相同,则一致;如果offset不同,则不一致,此时可以根据两个offset找出从节点缺少的那部分数据。例如,如果主节点的offset是1000,而从节点的offset是500,那么部分复制就需要将offset为501-1000的数据传递给从节点。而offset为501-1000的数据存储的位置,就是下面要介绍的复制积压缓冲区。

    (2)复制积压缓冲区

    复制积压缓冲区是由主节点维护的、固定长度的、先进先出(FIFO)队列,默认大小1MB;当主节点开始有从节点时创建,其作用是备份主节点最近发送给从节点的数据。注意,无论主节点有一个还是多个从节点,都只需要一个复制积压缓冲区。

    在命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量(offset)。由于复制积压缓冲区定长且是先进先出,所以它保存的是主节点最近执行的写命令;时间较早的写命令会被挤出缓冲区。

    由于该缓冲区长度固定且有限,因此可以备份的写命令也有限,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。反过来说,为了提高网络中断时部分复制执行的概率,可以根据需要增大复制积压缓冲区的大小(通过配置repl-backlog-size);例如如果网络中断的平均时间是60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见,可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制。

    从节点将offset发送给主节点后,主节点根据offset和缓冲区大小决定能否执行部分复制:

    • 如果offset偏移量之后的数据,仍然都在复制积压缓冲区里,则执行部分复制;
    • 如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤出),则执行全量复制

    (3)服务器运行ID(runid)

    每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成;runid用来唯一识别一个Redis节点。通过info Server命令,可以查看节点的runid:

    主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来;当断线重连时,从节点会将这个runid发送给主节点;主节点根据runid判断能否进行部分复制:

    • 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况);
    • 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制。

    主从复制过程

    1)首先,从节点根据当前状态,决定如何调用psync命令:

    • 如果从节点之前未执行过slaveof或最近执行了slaveof no one,则从节点发送命令为psync ? -1,向主节点请求全量复制;
    • 如果从节点之前执行了slaveof,则发送命令为psync ,其中runid为上次复制的主节点的runid,offset为上次复制截止时从节点保存的复制偏移量。

    (2)主节点根据收到的psync命令,及当前服务器状态,决定执行全量复制还是部分复制

    心跳机制

    在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:PING和REPLCONF ACK。从节点会向主节点定时的发送ping命令来检测主节点的超时情况,同时,从节点会向主节点发送 REPLCONF ACK 命令

    频率是每秒1次;命令格式为:REPLCONF ACK {offset},其中offset指从节点保存的复制偏移量。REPLCONF ACK命令的作用包括:

    (1)实时监测主从节点网络状态:该命令会被主节点用于复制超时的判断。此外,在主节点中使用info Replication,可以看到其从节点的状态中的lag值,代表的是主节点上次收到该REPLCONF ACK命令的时间间隔,在正常情况下,该值应该是0或1,如下图所示:

    img

    (2)检测命令丢失:从节点发送了自身的offset,主节点会与自己的offset对比,如果从节点数据缺失(如网络丢包),主节点会推送缺失的数据(这里也会利用复制积压缓冲区)。注意,offset和复制积压缓冲区,不仅可以用于部分复制,也可以用于处理命令丢失等情形;区别在于前者是在断线重连后进行的,而后者是在主从节点没有断线的情况下进行的。

    (3)辅助保证从节点的数量和延迟:Redis主节点中使用min-slaves-to-write和min-slaves-max-lag参数,来保证主节点在不安全的情况下不会执行写命令;所谓不安全,是指从节点数量太少,或延迟过高。例如min-slaves-to-write和min-slaves-max-lag分别是3和10,含义是如果从节点数量小于3个,或所有从节点的延迟值都大于10s,则主节点拒绝执行写命令。而这里从节点延迟值的获取,就是通过主节点接收到REPLCONF ACK命令的时间来判断的,即前面所说的info Replication中的lag值。

  • 相关阅读:
    使用 Dockerfile 定制镜像
    UVA 10298 Power Strings 字符串的幂(KMP,最小循环节)
    UVA 11090 Going in Cycle!! 环平均权值(bellman-ford,spfa,二分)
    LeetCode Best Time to Buy and Sell Stock 买卖股票的最佳时机 (DP)
    LeetCode Number of Islands 岛的数量(DFS,BFS)
    LeetCode Triangle 三角形(最短路)
    LeetCode Swap Nodes in Pairs 交换结点对(单链表)
    LeetCode Find Minimum in Rotated Sorted Array 旋转序列找最小值(二分查找)
    HDU 5312 Sequence (规律题)
    LeetCode Letter Combinations of a Phone Number 电话号码组合
  • 原文地址:https://www.cnblogs.com/blackmlik/p/12765961.html
Copyright © 2011-2022 走看看