1.什么是主从复制
在分布式服务中,副本和复制的思想不必多少,可以提升系统可用性,防止单点故障发生,redis则通过salve of命令允许某个redis实例成为另外一个redis实例的从节点来进行数据同步(redis采用异步复制)。
复制机制包括这三个特性:
- master和salve正常连接时,master会发出一连串的命令流对salve节点进行更新,内容包括操作命令、key的过期淘汰
- 两者连接断开后,salve重连master需要尝试进行部分重同步以减小全量同步的开销(只获取断开期间丢失的命令流)。
- 当无法进行部分重同步时就需要master生成rdb文件进行全量同步,同时持续发送这期间产生的命令流。
主从复制的优点
- master节点可以关闭持久化,减少io操作以降低延迟
- salve节点可以完成读命令,这样提高处理的并发量,当然由于主从复制数据肯定会存在短暂的不一致。(在我之前提到的例子中,我造成了一个大value,当时在线上的操作会导致整个实例pending,这种情况要想在不发版的情况下解决就需要增加副本而不能增加集群节点,因为副本数据是同样存在的)
- 防止单点故障,master节点挂掉后还可以进行选举产生新的maste节点,保证对外服务可用。
主从复制和集群的区别
主从复制:主要是为了防止单点故障和增加并发读能力,复制的机制中存在一主多从,数据只能被master处理,处理命令会以数据流方式发送到slave节点进行同步。
集群:集群,可以理解为对数据的拆分,当数据量达到一定程度就不可以再使用单一主从模式存储数据,数据会根据key计算(hash)出的slot值在多个分片上进行分区,客服端对某个key的写入/读取操作需要先映射到某个分片(每个分片占据一定数量的slot),集群是分区和主从复制的结合,即存在多个分区,每个分区由一主多从组成。
主从复制的流程
- 复制初始化:slave节点通过配置的master节点信息发起建立socket请求,master存储连接信息,然后slave发出ping,master响应pong表示连接建立成功,如果未响应或超时则slave重试重新建立socket连接。
- 数据同步阶段:确认双方信息后即可进行第一次数据同步,slave 向主库发送 psync 命令,master判断执行全量or部分同步命令,之后再有新的客户端请求,进入下一阶段。
- 命令传播阶段:repl-ping-slave-period 指定master每隔多长时间向slave发送心跳请求来判断是否存活(默认十秒),slave每秒都向master发送REPLCONF ACK并携带自己复制的偏移量(这样master据此来判断发送哪些同步命令)。
其中主从复制采用psync命令,这个命令在4.0前后升级改造存在一定差异
4.0版本之前
全量同步
master生成并发送rdb文件,然后将缓冲区中的命令发送,即数据同步和命令传播
部分同步
适用于断线重连情况,slave只需要接受断线期间的命令,不需要全量同步,实现方式为复制偏移量(offset)、复制积压缓冲区(replication backlog buffer)和运行 ID (run_id)。
复制偏移量:master和slave各自维护有复制偏移量,每次发送/接受同步数据,就会对偏移量++,这样可以通过偏移量对比数据同步间的差异
复制积压缓冲区:由master维护的FIFO(先入先出)队列,默认1mb,master在命令传播时不但发送到slave还会将命令保存到复制积压缓冲区,断线重连时,master会判断salve这期间丢失的命令是否存在于复制积压缓冲区,如果存在即可通过部分同步进行恢复,否则要进行全量同步。
运行 ID:用于区分master节点,在断线重连时可能master节点已经更换,这种情况下自然不存在所谓的复制积压缓存区,所以可以用于区分,salve初次复制(rfb)时master就会将自己的runId发送给slave并进行保存,这样重连时master可以通过比对自身的runid和slave保存的上一次同步的runid,判断是否可以部分同步。
流程:
salve第一次执行slave of命令,首先会发送 PSYNC ? -1命令,主动请求进行全量同步(此时不可能存在部分同步),反之如果是断线重连,会发送 PSYNC 命令,由master判断执行何种操作。
- 如果 master 返回 +FULLRESYNC 回复,那么表示 master 将与 slave 执行完整重同步操作:其中 runid 是这个 master 的运行 ID,slave 会将这个 ID 保存起来,在下一次发送 PSYNC 命令时使用;而 offset 则是 master 当前的复制偏移量,slave 会将这个值作为自己的初始化偏移量
- 如果 master 返回 +CONTINUE 回复,那么表示 master 将与 slave 执行部分同步操作,slave 只要等着 master 将自己缺少的那部分数据发送过来就可以了
- 如果 master 返回 -ERR 回复,那么表示 master 的版本低于 Redis 2.8,它识别不了 psync 命令,slave 将向 master 发送 SYNC 命令,并与 master 执行完整同步操作
4.0版本之后
4.0版本之后最大的变化支持两种场景下的部分同步,一个场景是slave提升为master后,其他slave可以从新提升的master上进行部分同步,另一个是salve重启后可以进行部分同步。
slave在意外关闭的时候会把当前对应master的runId和复制偏移量offset保存到rdb文件中,这样重启恢复数据就可以拿到,重连master后发送psync命令由master判断是否可以进行部分同步。
同事又引入了一个runId2字段,用于存放同步过的master节点的runId,当节点从 slave 提升为 master 后,会保存两个复制 ID(之前角色是 slave 的时候 replid2 没用,现在要派上用场了),分别是 replid(当前的masterId) 和 replid 2(之前同步的masterId),其他 slave 复制的时候可以根据第二个复制 ID 来进行部分重同步。