一、什么是主从复制
在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他服务器,满足故障恢复和负载均衡的需求,Redis也是如此,可以通过SLAVEOF命令或者通过配置文件设置slaveof选项,可以让一台服务器去复制另一台服务器,其中被复制的服务器叫主服务器(master),而对主服务器进行复制的服务器叫从服务器(slave),从而实现主服务器中的数据更新后,根据配置和策略自动同步到从服务器,其中master以写为主,slave以读为主。
二、建立复制
每个从节点只能有一个主节点,而主节点可以同时具有多个从节点,复制的数据是单项的,只能从主节点复制到从节点,配置复制的方式有以下三种
- 在配置文件中加入slaveof {masterHost} {masterPort}
- 在redis-server启动命令后加入–slaveof {masterHost} {masterPort}
- 使用命令slaveof {masterHost} {masterPort}生效
下面开启两个端口为6379和6380的Redis节点,在6380中执行slaveof命令。让6379作为主节点,6380作为从节点。
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380>
当设置成功后,在6379中执行如下命令测试。
127.0.0.1:6379> set name test
OK
127.0.0.1:6379> set age 100
OK
127.0.0.1:6379>
然后回到6380中查看上面两个值。
127.0.0.1:6380> get name
"test"
127.0.0.1:6380> get age
"100"
127.0.0.1:6380>
从运行结果中看到复制已经工作了,针对主节点6379的任何修改都可以同步到从节点6380中。
主节点复制成功建立后,可以使用info replication命令查看复制相关状态。
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=520,lag=1
master_replid:d800e88d8aa8d117db4d2b594ae7839551a04126
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:520
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:520
127.0.0.1:6379>
三、断开复制
slaveof命令不但可以建立复制,还可以在从节点执行slaveof no one来断开与主节点的复制关系,从节点断开复制后并不会抛弃原有的数据,只是无法再获取主节点上的数据变化,通过slaveof命令还可以实现切主操作,所谓切主是指把当前从节点对主节点的复制切换到另一个主节点。
四、传输延迟
主从节点一般部署在不同机器上,复制时的网络延迟就成为需要考虑的问题,Redis为我们提供了repl-disable-tcp-nodelay参数用于控制是否关闭TCP NODELAY, 默认关闭。
当关闭时,主节点产生的命令数据无论大小都会及时地发送给从节点,这样主从之间延迟会变小,但增加了网络带宽的消耗。适用于主从之间的网络环境良好的场景,如同机架或同机房部署。
当开启时,主节点会合并较小的TCP数据包从而节省带宽。默认发送时间间隔取决于Linux的内核,一般默认为 40毫秒。这种配置节省了带宽但增大主从之间的延迟。适用于主从网络环境复杂或带宽紧张的场景,如跨机房部署。
五、复制过程
复制过程大概分为六个步骤,
-
保存主节点(master)信息
执行slaveof后从节点只保存主节点的地址信息便直接返回,这时建立复制流程还没有开始。 -
建立连接
从节点通过内部每秒运行定时任务维护复制相关的逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络一个socket套接字,建立连接后从节点会打印以下日志。
Connecting to MASTER 127.0.0.1:6379
MASTER <-> REPLICA sync started
如果从节点无法建立连接,定时任务会无限制重试直到连接成功或者执行slaveof no one取消复制,并且,可以在从节点执行info replication查看master_link_down_since_seconds指标,它会记录与主节点连接失败的系统时间,还会每秒打印如下日志,也方便我们发现问题。
Jun 2020 15:10:33.234 # Error condition on socket for SYNC: Operation now in progress
- 发送ping
连接建立成功后从节点发送ping请求进行首次通信,主要有两个目的。
- 检测主机之间的网络套接字是否可用。
- 检测主节点当前是否可接收处理命令。
主服务器在接收到PING命令后,会有不同的返回结果,其体如下。
-
如果主服务器向从服务器返回一个命令回复, 此时如果从服务器在规定的时间内不能读取出这条命令,就表示主从服务器之间的网络连接状态不好, 不能维续完成复制工作。出现这种情况,从服务器会断开与主服务器的连接,后续需要重新建立套接字连接。
-
如果主服务器向从服务器返回一个错误,就表示主服务器暂时不能处理从服务器发送过来的命令请求,不能继续完成复制工作。出现这种情况,从服务器会断开与主服务器的连接,后续需要重新建立套接字连接。
-
如果主服务器向从服务器返回“PONG",就表示主从服务器之间的网络连接状态良好,同时主服务器可以正常处理从服务器发送过来的命令请米,并能完成复制工作。
- 权限认证
如果主节点设置了requirepass参数,则需需要密码认证,从节点必须配置masterauth参数保证与主节点相同的密码才能通过认证,如果验证失败复制将终止。 - 同步数据集
主从复制连接正常通信后,对于首次建立复制的场景,主节点会把所有的数据全部发送给从节点,这部分操作是耗时最长的,Redis在2.8版本以后采用复制新命令psync进行数据同步,原来的sync命令依然支持,保持新旧版的兼容性。 - 命令持续复制
当主节点把当前的数据同步给从节点后,便完成了复制的建立流程,接下来主节点会持续的把写命令发送给从节点,保证主从数据一致性。
六、复制功能的原理
自Redis 2.8版本以后,开始使用SYNC命令代替SYNC命令来执行复制时的同步操作。PSYNC命令具有全量同步(也叫全量复制)和部分同步(也叫部分复制)两种模式。
-
全量同步:用于处理第一次复制的情况,它通过让主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区里面的写命令来进行同步。
-
部分同步:用于处理从服务器离线后重新连接的复制情况。当从服务器在离线后重新连接到主服务器时,如果条件允许,那么主服务器可以将在从服务器断开连接期间执行的最新写命令发送给从服务器,从服务器在接收并执行这些写命令后,就可以将数据库更新到与主服务器相同的状态,从而达到主从服务器数据库状态的一致性。
PSYNC命令的部分同步模式解决了Redis 2.8以前版本的复制功能在处理离线后重复制时出现的低效问题。
SYNC和PSYNC命令都可以实现让离线的主从服务器重新回到一致性状态,但是,在执行部分同步操作时,使用PSYNC命令所需要的资源远远少于使用SYNC命令所使用的资源,并且完成同步的速度也快很多。执行SYNC命令需要生成、发送和加载整个RDB文件,而执行PSYNC命令进行部分同步时,只需要将从服务器缺少的写命令发送过来就可以了。
在执行复制操作的时候,主从服务器都会有一个复制偏移量。
当主服务器每次向从服务器发送N字节的数据时,就会将自己的复制偏移量的值加上N,而当从服务器每次接收到主服务器发送过来的N字节数据时,就会将自己的复制偏移量的值加上N.
可以使用INFO rplication命令来查看主服务器的复制信息,master_repl_offset为主服务器的复制偏移量,这里的是3628,并且他有一台从服务器,从服务器的复制偏移量也都为3628。
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=3628,lag=0
master_replid:d800e88d8aa8d117db4d2b594ae7839551a04126
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:3628
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:3628
127.0.0.1:6379>
可以根据主从复制的偏移量确认主从服务器的数据库是否处于一致状态,如果一致,那么主从服务器两者的复制偏移量总是相等的,相反,说明主从服务器处于不一致状态。
七、主从服务器的运行ID
每台Redis服务器都会有自己的运行ID,运行ID在服务器启动的时候会自动生成,由40个十六进制随机组成。
当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID发送给从服务器,从服务器接收到主服务器的运行ID时,会将它保存起来。如果从服务器发生离线。那么,当再次连接到主服务器时,从服务器将向主服务器发送之前保存的运行ID,进行身份验证。
如果从服务器保存的运行ID与当前所连线的主服务器的运行ID相同,说明原从服务器离线之前连接的就是这台主服务器,主服务器可以继续执行部分同步操作。
如果从服务器保存的运行ID与当前所连接的主服务器的运行ID不相同,则说明从服务器离线之前连接的并不是这台主服务器,此时主服务器会对这台从服务器执行全量同步操作。
八、Redis心跳机制
在主从服务器之间进行命令传播的时候,从服务器默认会以每秒一次的频率发送命令REPLCONF ACK <rplication ofset>
到主服务器,用于检测主从服务器之间的网络连接情况。其中,replication ofiset是从服务器当前的复制偏移量。
从服务器发送REPLCONF ACK命令主要有以下几个作用。
- 检测主从服务器之间的网络连接情况。
从服务器通过向主服务器发送REPLCONF ACK命令来检测主从服务器之间的网络连接情况。如果主服务器超过1秒没有接收到从服务器发送过来的命令,就会认为网络连接出现了问题。
- 检测在命令传播过程中是否有命令丢失。
在主从服务器进行命令传播的过程中,可能会因为网络的故障而导致传输的写命令在途中丢失,这样就会导致主从服务器的复制偏移量不一致。当从服务器向主服务器发送REPLCONF ACK命令时,如果主服务器发现从服务器的复制偏移量与自己的复制偏移量不一致,就会根据从服务器的复制偏移量,在复制缓冲区中找到发送过程中所丢失的写命令,然后补发给从服务器,实现同步功能。