redis-复制
一、复制介绍
主从复制,是把一台redis服务器上数据复制到其他服务器的机制,其中前者被称为主节点(master),后者被称为从节点(slave)。
主从复制的主要主要作用:
- 数据冗余:数据热备,多机备份。
- 故障恢复:当主节点出现问题时,可以让从节点提供服务,是一种功能的冗余。
- 负载均衡:可以让主节点写,从节点多,可以把压力分配到多个从节点,从而实现负载均衡。
- 高可用基石:主从复制是实现哨兵和集群的基础。
默认情况下,每个redis服务器都是master节点,每一个master可以有多个slave节点,但是一个salve节点只能有一个master节点。
二、复制配置
2.1 建立复制
2.1.1 命令
- 在配置文件中加入:slaveof
- 在redis-server启动命令后加入 -- slaveof
- 直接使用命令(在客户端执行): slaveof
2.1.2 演示
准备节点
默认6380端口作为master节点,再启动一个6380节点作为salve节点。
复制一个配置文件,命名为redis6380.conf
配置新的端口号
两个redis实例已经启动。
执行复制命令
启动两个实例
执行slaveof命令
查看效果
master节点写入,读取
slave节点读取数据
至此,复制搭建成功,数据已经成功从master复制到了slave,并且通过salve读取成功。
2.2 断开复制
3.2.1 直接断开
直接使用slaveof no one命令即可断开和master的复制关系
断开复制关系后的数据
- 原有已经复制的数据会保留
- master后续写入的数据将不再同步
可以看到原有的数据保留了
断开后在原有master写入
数据将不再同步到6380
3.2.2 切换到其他master
可以通过切换到其他master的方式断开和当前master的绑定。但是和slave no one不同的是,切换新的master后,从原有master复制过来的数据会被清空。
三、拓扑结构
一对一,一对多,树状结构。
四、复制过程
4.1保存主节点
执行slaveof后从节点只是保存了主节点的地址信息变直接返回,复制流程还没有正式开始。
4.2 主从建立socket连接
从节点内部通过每秒运行的定时任务来处理相关逻辑,当定时任务发现存在新的主节点后,会尝试和主节点建立网络连接。
从节点会创建一个socket去连接主节点,后续数据同步都是基于这个socket进行。
如果从节点无法建立连接,定时任务会无限重试到连接成功或者复制被取消为止。
4.3 发送ping命令
连接建立成功后从节点会向主节点发送ping请求进行首次通信,主要有如下目的:
- 检测之前建立的socket是否可用。
- 检测主节点当前是否可接受处理命令
发送ping命令后如果从节点没有收到pong回复或者超时(比如网络超时,或者主节点阻塞无法处理等),从节点会断开复制,下次定时任务发起后重新连接。
4.4 权限验证
如果主节点设置了requirepass参数,则需要密码验证,从节点必须配置masterauth参数保证与主节点相同的密码才能通过验证,如果验证失败,从节点会断开复制,下次定时任务发起后重新连接。
4.5 数据同步
主从复制连接建立成功后,便开始数据同步,属于数据的初始化,主节点会把持有的所有数据发送给从节点,主题是实现方式是从节点给主节点发送psync命令(2.8之前是sync命令)。这块是耗时最长的步骤,分为全量同步和部分同步。
4.6 命令持续同步
当主节点把当前的数据同步给从节点后,便完成了复制的建立流程,后续主节点会持续的把命令发送给从节点,保证主从一致。
五、数据同步原理
主从建立连接成功后,从节点会向主节点发送psync命令来完成数据同步,同步过程分为:全量复制和部分复制。
- 全量复制:一般用于初次复制场景,主节点一次性把全部数据发给从节点,是一个比较重的操作
- 部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失的场景,当主从再次连接后,如果主节点完整保存了中断期间的数据,主节点会补发丢失的数据给从节点,补发的数据远远小于全量数据,部分复制有效避免了全量复制的过高开销。
5.1 psync命令需要的组件
psync命令运行需要以下组件的支持:
- 主从复制偏移量
- 主节点复制挤压缓冲区
- 主节点运行id
5.1.1 主从复制偏移量
master节点处理完写入命令后,会把命令的字节长度做累加记录。
从节点再接收到主节点发送的命令后,也会累加自身的偏移量。
通过对比master的偏移量和slave的偏移量来看slave和master的数据差异大小。
5.1.2 主节点复制积压缓冲区
复制缓冲区是保存在主节点上的一个固定长度队列,默认大小为1MB,当有slave时候回创建缓冲区,这时主节点响应写命令时,不但会把命令发送给从节点,还会写入复制挤压缓冲区。
挤压缓冲区是一个先进先出的队列,如果超过容量,之前的数据会被覆盖。大小是可以配置的。挤压缓冲区主要为了部分复制做准备。可以通过info replication来查看:
repl_backlog_active:1 //开启复制缓冲区
repl_backlog_size:1048576 //缓冲区最大长度
repl_backlog_first_byte_offset:4505 //起始变异量,计算当前缓冲区可用范围
repl_backlog_histlen:5460 //已保存数据的有效长度
5.1.3 主节点运行id
每个redis节点(主从)启动后都会生成一个40位的16进制的字符串作为运行id,用于唯一识别一个redis节点。从节点会保存主节点的运行id用于识别自己正在复制的是哪一个主节点。redis重启后id会改变。
初次复制时,从节点会保存主节点的runid。
5.2 psync命令
从节点通过给主节点发送psync命令实现部分复制或者全量复制。命令格式为:
psync {runid} {offset}
- runid:所辅助主节点的runid,
- offset:当前从节点的数据偏移量
第一次复制时没有offset和主节点的runid,会发送psync -1命令
5.3 全量复制
- 首次复制发送 psync ? -1
- master 继续请求发现是全量复制,恢复fullresync
- slave保存主节点响应的runid和 offset
- 主节点执行bgsave,并且把从选择开始的命令同时写入一个缓冲区(复制缓冲区)
- 主节点执行完bgsave后把最终生成的rdb发给salve。
- salve收到主节点发送过来的rdb后开始清空自身数据
- salve把主节点的rdb载入自己的rdb,此时salve的数据更新至主节点执行bgsave时候的状态。
- 主节点将复制缓冲区的命令发送给salve
- slave执行接收到的复制缓冲区的命令,至此salve的数据更新至主节点的最新状态
- 如果slave开启了aof,则会立即做bgrewriteaof,确保全量复制后aof持久化文件立即可用。
5.4 部分复制
部分复制时redis针对全量复制开销过高做出的一种优化措施,使用psync {runid}{offset}命令实现。
当主从复制的过程中,如果出现网络闪断或者命令丢失等异常情况,从节点会要求主节点补发数据,如果此时主节点的复制积压缓冲区内存中刚好存在这部分数据(就是断网这段时间没有同步到从节点的数据),则直接发给从节点,最终保持了和从节点的一直,也避免了大规模的全量复制。
- 如果主从节点之间网络出现中断,如果超过repl-timeout时间,主节点会认为从节点故障并中断复制连接。
- 主从断开后,主节点依然在响应命令,这个时候新的命令无法同步到从节点,主从出现了不一致。上面也解释了主节点会默认将命令写入到复制积压缓冲区,默认为1M,超出后会覆盖。
- 网络恢复了,从节点再次连接主节点,连接建立成功。
- 从节点保存了主节点的runid和自身的复制偏移量(offset),通过psync {runid} {offset}命令和主节点进行交互
- 主节点如果发现满足部分复制的条件(后面详细解释这个条件),则返回continue给从节点
- 主节点根据偏移量把复制积压缓冲区的数据发给从节点,最终保证主从复制进入正常。
主节点判断判断满足部分复制的条件
- runid必须和自身一致
- 从节点发过来的偏移量之后的数据都在自身的复制积压缓冲区内,这个很好理解,比如从节点发过来的offset是10(代表10以后的数据都没有同步),但是缓冲区的offset是15(说明15之前的的数据已经不在缓冲区了),这个时候就没办法进行部分复制
如果不满足部分复制条件,则主节点会返回fullsync给从节点,从节点会开启全量复制。
由此可见复制积压缓冲区的大小比较重要,如果太小,会被覆盖,最终导致主从网络恢复后无法进行部分复制,这个值得大小应该要基于网络的中断时间,已经主节点的qps和命令的大小来进行计算,然后进行合理的设置。
六、主从心跳
6.1 流程
主从心跳检测示意图
-
master会周期性的ping slave,周期时间通过repl-ping-replica-period参数来控制,默认是10秒
-
slave每隔1秒回向master发送replconf ack {offset}命令:
-
实时监测主从节点的网络状态
-
上报自身的数据复制偏移量,如果主节点发现从节点有数据缺失,主节点会从自身的复制积压缓冲区中拉取数据发给从节点。
-
实现从节点的数量和延迟性功能,通过min-replicas-to-write(最小可用的从节点个数)和min-replicas-max-lag(允许的最小延迟秒,一般为0,或者1)参数定义。
-
如果master开启了这两个参数,那么如果可用的从节点小于min-replicas-to-write或者延迟大于min-replicas-max-lag,master会拒绝数据写入。示意图如下。
-
-
6.2 repl -timeout参数
redis.conf有个repl-timeout参数:
-
slave角度,如果在repl-timeout时间内没有收到传输的rdb snapshot数据,
-
slave角度,如果在repl-timeout没有收到master发送的数据包或者ping。
-
master角度,如果在repl-timeout时间没有收到REPCONF ACK确认信息。
当redis检测到repl-timeout超时(默认值60s),将会关闭主从之间的连接,redis slave会重新建立主从连接的请求。这个值一定要大于repl-ping-replica-period参数
为了降低主从延迟,一般建议把redis的主从节点部署在相同的机房。
七、全量复制场景
全量复制非常重,应该尽量避免,下面是一些会导致全量复制的操作。
- 第一次建立复制,无法避免,建议低峰时候进行
- runid不匹配,从节点会保存主节点的runid,如果主节点重启,则主节点的runid会改变,发现和从节点保存的runid不一致时,会进行全量复制,应该避免重启,比如可以采用debug reload命令,或者采用故障转移功能,当主节点发生故障后,可以将从节点提升为主节点,或者采用哨兵或者集群方案
- 复制积压缓冲区不足(repl-backlog-size),这个缓冲区默认大小为1M,当超过1m后覆盖,主从中断再次连接后如果从节点的offset在复制积压缓冲区找不到,则会导致全量复制,这个缓冲区的大小要基于网络状况,命令大小,以及qps进行计算配置。
七、一些配置和命令
- salve-read-only=yes。 从节点只读,如果从节点修改,会造成主从数据不一致
- repl-disable-tcp-nodelay 是否关闭tcp_nodelay,默认为no,建议配置为yes。这个是服务器tcp的一个功能,tcp nagle算法
- debug reload,不会导致runid改变,但是会情况内存数据,再次从rdb加载。
- 。。。