redis主从复制
1、 redis主从复制
1.0 背景
在实际的场景当中单一节点的redis容易面临风险。 比如:
机器故障。我们部署到一台 Redis 服务器,当发生机器故障时,就无法进行数据的写入跟读取,这时需要迁移到另外一台服务器并且要保证数据是同步的。
要实现分布式数据库的更大的存储容量和承受高并发访问量, 我们会将原来集中式数据库的数据分别存储到其他多个网络节点上。
Redis 为了解决这个单一节点的问题, 也会把数据复制多个副本部署到其他节点上进行复制, 实现 Redis 的高可用, 实现对数据的冗余备份, 从而保证数据和服务的高可用。
1.1 什么是主从复制
主从复制, 是指将一台Redis服务器的数据, 复制到其他的Redis服务器。 前者称为主节点(master), 后者称为从节点(slave),数据的复制是单向的, 只能由主节点到从节点。默认情况下, 每台Redis服务器都是主节点, 且一个主节点可以有多个从节点(或没有从节点), 但一个从节点只能有一个主节点。
1.2 主从复制作用
主从复制的作用主要包括:
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
- 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
1.3 主从配置
redis对于主从复制有三种方式
1. 在配置文件中加入 slaveof {masterHost} {masterPort} 随redis启动生效
2. 在redis-server启动命令后加入 --slaveof {masterHost} {masterPort} 生效
3. 直接使用命令: slaveof {masterHost} {masterPort}
主从连接效果:

查看主从连接信息 命令 info replilcation
主:-----------------------------
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.16.16.140,port=6379,state=online,offset=910,lag=1
master_replid:dcb74b42b1b8ab7cc2c6588c38adbbeb648a905f
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:910
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:910
从:-----------------------------
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:172.16.16.150
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:1050
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:dcb74b42b1b8ab7cc2c6588c38adbbeb648a905f
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1050
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:659
repl_backlog_histlen:392
从节点断开复制
slaveof命令不但可以建立复制, 还可以在从节点执行 slaveof no one 来断开与主节点复制关系。 如果从节点断开了连接, 那么当前的redis则会切换为主服务

1.4 注意
1.4.1 安全:
对于数据比较重要的节点, 主节点会通过设置requirepass参数进行密码验证, 这时所有的客户端访问必须使用auth 命令实行验证。 从节点与主节点的复制链接是通过一个特殊标识的客户端来完成, 因此需要配置从节点的masterauth参数与主节点密码保持一致, 这样从节点才可以正确地链接到主节点并发起复制流程
1.4.2 从节点只读:
默认情况下slave-read-only=yes配置为只读。 由于复制只能从主节点到从节点, 对于从节点的任何修改主节点都无法感知, 修改从节点数据会造成主从数据不一致。 因此没必要就不要动这个配置
1.4.3. 网络延迟问题:
主从节点一般部署在不同机器上, 复制时的网络延迟就成为需要考虑的问题, Redis 为我们提供了repl-disable-tcp-nodelay参数用于控制是否关闭TCP NODELAY,默认关闭, 说明如下:
- 当关闭时, 主节点产生的命令数据无论大小都会及时地发送给从节点, 这样主从之间延迟会变小, 但增加了网络带宽的消耗。 适用于主从之间的网络环境良好的场景, 如同机架或同机房部署。
- 当开启时, 主节点会合并较小的TCP数据包从而节省带宽。 默认发送时间间隔取决于Linux的内核, --般默认为40毫秒。 这种配置节省了带宽但增大主从之间的延迟。 适用于主从网络环境复杂或带宽紧张的场景, 如跨机房部署。
注意: 运提部署主从节点时需要考虑网络延迟、 带宽使用率、 防灾级别等因素, 如要求低延迟时, 建议同机架或同机房部署并关闭repl-disable-tcp-nodelay;如果考虑高容灾性, 可以同城跨机房部署并开启repl-disable-tcp-nodelay
2、主从结构类型

3、原理

根据上图分析大致可以分为6个过程:
1. 保存主节点(master)
执行slaveof后从节点只保存主节点的地址信息便直接返回, 这时建立复制流程还没有开始
2. 从节点(slave) 内部通过每秒运行的定时任务维护复制相关逻辑, 当定时任务发现存在新的主节点后, 会尝试与该节点建立网络连接
从节点会建立一个 socket 套接字, 从节点建立了一个端口为 51234 的套接字, 专门用于接受主节点发送的复制命令。
如果从节点无法建立连接, 定时任务会无限重试直到连接成功或者执行 slaveof no one 取消复制
关于连接失败, 可以在从节点执行 info replication 查看 master_link_down_since_seconds 指标, 它会记录与主节点连接失败的系统时间。 从节点连接主 节点失败时也会每秒打印如下日志, 方便发现问题:
Error condition on socket for SYNC: {socket_error_reason}

3. 发送 ping 命令
连接建立成功后从节点发送 ping 请求进行首次通信, ping 请求主要目的如下:
* 检测主从之间网络套接字是否可用。
* 检测主节点当前是否可接受处理命令。
如果发送 ping 命令后, 从节点没有收到主节点的 pong 回复或者超时, 比如网络超时或者主节点正在阻塞无法响应命令, 从节点会断开复制连接, 下次定时任务会发起重连

4. 权限验证
如果主节点设置了 requirepass 参数, 则需要密码验证, 从节点必须配置 masterauth 参数保证与主节点相同的密码才能通过验证; 如果验证失败复制将终止, 从节点重新发起复制流程。
5. 同步数据集
主从复制连接正常通信后, 对于首次建立复制的场景, 主节点会把持有的数据全部发送给从节点, 这部分操作是耗时最长的步骤。
6. 命令持续复制
当主节点把当前的数据同步给从节点后, 便完成了复制的建立流程。 接下来主节点会持续地把写命令发送给从节点, 保证主从数据一致性。
注意:主从在同步的过程当中, 会把原本的从节点的数据清空
4、数据同步
4.1 概念:
全量复制:
用于初次复制或其它无法进行部分复制的情况, 将主节点中的所有数据都发送给从节点, 是一个非常重型的操作, 当数据量较大时, 会对主从节点和网络造成很大的开销
部分复制:
用于处理在主从复制中因网络闪断等原因造成的数据丢失场景, 当从节点再次连上主节点后, 如果(条件允许), 主节点会补发丢失数据给从节点。 因为补发的数据远远小于全量数据, 可以有效避免全量复制的过高开销, 需要注意的是, 如果网络中断时间过长, 造成主节点没有能够完整地保存中断期间执行的写命令, 则无法进行部分复制, 仍使用全量复制
4.2复制偏移量
参与复制的主从节点都会维护自身复制偏移量。 主节点(master) 在处理完写入命令后, 会把命令的字节长度做累加记录, 统计信息在 info relication 中的 master_repl_offset 指标中:
从节点(slave) 每秒钟上报自身的复制偏移量给主节点, 因此主节点也会保存从节点的复制偏移量.

4.3 复制积压缓冲区
复制积压缓冲区是保存在主节点上的一个固定长度的队列, 默认大小为1MB, 当主节点有连接的从节点(slave) 时被创建, 这时主节点(master) 响应写命令时, 不但会把命令发送给从节点, 还会写入复制积压缓冲区

在命令传播阶段, 主节点除了将写命令发送给从节点, 还会发送一份给复制积压缓冲区, 作为写命令的备份; 除了存储写命令, 复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量(offset) 。 由于复制积压缓冲区定长且先进先出, 所以它保存的是主节点最近执行的写命令; 时间较早的写命令会被挤出缓冲区()。
4.4 主节点运行ID
每个Redis节点启动后都会动态分配一个40位的十六进制字符串作为运行ID。 运行ID的主要作用是用来唯一识别 Redis节点, 比如从节点保存主节点的运行ID识别自己正在复制的是哪个主节点。 如果只使用ip+port的方式识别主节点, 那么主节点重启变更了整体数据集(如替换RDB/AOF文件), 从节点再基于偏移量复制数据将是不安全的, 因此当运行ID变化后从节点将做全量复制。 可以运行info server命令查看当前节点的运行ID:
127.0.0.1:6379> info server
# Server
redis_version:5.0.7
...
run_id:09f134f7ae60dd091e6d59fc7e93fe0c82bb48d8
需要注意的是Redis关闭再启动, 运行的id会随之变化
4.5 Psync命令
从节点使用psync命令完成部分复制和全量复制功能 psync runid offset
流程说明:
- 从节点(slave)发送psync命令给主节点, 参数runid是当前从节点保存的主节点运行id, 如果没有则默认值为 '?' , 参数offset是当前从节点保存的复制偏移量, 如果是第一次参与复制则默认值为-1
- 主节点(master)根据psync参数和自身数据情况决定响应结果:
- 如果回复+FULLRESYNC {runid} {offset}, 那么从节点将触发全量复制流程。
- 如果回复+CONTINUE, 从节点将触发部分复制流程。
- 如果回复+ERR, 说明主节点版本低于Redis2.8
5. 全量复制流程
全量复制是Redis最早支持的复制方式, 也是主从第一次建立复制时必须经历的阶段。 触发全流量复制的命令是sync和psync

流程说明
- 发送psync命令进行数据同步, 由于是第一次进行复制, 从节点没有复制偏移量和主节点的运行id, 所以发送psync ? -1
- 主节点根据psync ? -1解析出当前为全量复制, 回复+FULLRESYNC响应(主机会向从机发送 runid 和 offset, 因为 slave 并没有对应的 offset, 所以是全量复制)
- 从节点接收主节点的响应数据保存运行ID和偏移量offset(从机 slave 会保存 主机master 的基本信息 save masterInfo)
- 主节点收到全量复制的命令后, 执行bgsave(异步执行) , 在后台生成RDB文件(快照) , 并使用一个缓冲区(称为复制缓冲区) 记录从现在开始执行 的所有写命令
- 主节点发送RDB文件给从节点, 从节点把接收到的RDB文件保存在本地并直接作为从节点的数据文件, 接收完RDB后从节点打印相关日志, 可以在日志中查看主节点发送的数据量(主机send RDB 发送 RDB 文件给从机)
MASTER <-> REPLICA sync: Flushing old data
注意! 对于数据量较大的主节点, 比如生成的RDB文件超过6GB以上时要格外小心。 传输文件这一步操作非常耗时, 速度取决于主从节点之间网络带宽, 通过细致分析Full resync和MASTER <-> SLAVE这两行日志的时间差, 可以算出RDB文件从创建到传输完毕消耗的总时间。 如果总时间超过repl-timeout所配置的值(默认60秒), 从节点将放弃接受RDB文件并清理已经下载的临时文件, 导致全量复制失败;针对数据量较大的节点, 建议调大repl-timeout参数防止出现全量同步数据超时;
例如对于千兆网卡的机器, 网卡带宽理论峰值大约每秒传输100MB,在不考虑其他进程消耗带宽的情况下, 6GB的RDB文件至少需要60秒传输时间, 默认配置下, 极易出现主从数同步超时。 - 对于从节点开始接收RDB快照到接收完成期间, 主节点仍然响应读写命令, 因此主节点会把这期间写命令数据保存在复制客户端缓冲区内, 当从节点加载完RDB文件后, 主节点再把缓冲区内的数据发送给从节点, 保证主从之间数据致性。 (发送缓冲区数据)
- 从节点接收完主节点传送来的全部数据后会清空自身旧数据(刷新旧的数据, 从节点在载入主节点的数据之前要先将老数据清除)
- 从节点清空数据后开始加载RDB文件, 对于较大的RDB文件, 这一步操作依然比较消耗时间, 可以通过计算日志之间的实际差来判断加载RDB的总消耗时间(加载 RDB 文件将数据库状态更新至主节点执行bgsave时的数据库状态和缓冲区数据的加载。 )
114:S 28 Apr 2020 18:57:33.537 * MASTER <-> REPLICA sync: Loading DB in memory
114:S 28 Apr 2020 18:57:33.537 * MASTER <-> REPLICA sync: Finished with success
- 从节点成功加载完RDB后, 如果当前节点开启了AOF持久化的功能, 它会立刻做bgrewriteeaof的操作, 为了保证全量复制后AOF持久化文件立刻可用。
通过分析全量复制的所有流程, 全量制是一个非常耗时费力的操作。 他的实际开销主要包括:
- 主节点bgsave时间
- RDB文件网络传输时间。
- 从节点清空数据时间
- 从节点加载RDB的时间
- 可能的AOF重写时间
6.部分复制流程
部分复制是 Redis 2.8 以后出现的, 之所以要加入部分复制, 是因为全量复制会产生很多问题, 比如像上面的时间开销大、 无法隔离等问题, Redis 希望能够在master 出现抖动(相当于断开连接) 的时候, 可以有一些机制将复制的损失降低到最低

流程说明:
- 当主从节点之间网络出现中断时, 如果超过repl-timeout时间, 主节点会认为从节点出问题了并断开复制链接(如果网络抖动(连接断开 connection lost) )
- 主从连接中断期间主节点依然响应命令, 但因复制链接中断命令无法发送给从节点,不过主节点内部存在的复制积压缓存区, 依然可以保存一段时间的写命令数据, 默认最大缓存1MB(主机master 还是会写 replbackbuffer(复制缓冲区) )
- 当主从节点网络恢复后, 从节点会再次连上主节点。 (从机slave 会继续尝试连接主机)
- 当主从连接恢复后, 由于从节点之前保存了自身已复制的偏移量和主节点的运行id。 因此会把他们当作psync参数发送给主节点, 要求进行部分复制操作。 (从机slave 会把自己当前 runid 和偏移量传输给主机 master, 并且执行 pysnc 命令同步)
- 主节点接到psync命令后首先核对参数的runid, 如果 master 发现你的偏移量是在缓冲区的范围内, 根据参数offset在缓冲区查找复制, 如果在偏移量之后的数据存在缓存区中, 则对从节点发送continue表示可以进行部分复制
- 主节点根据偏移量把复制积压缓冲区里的数据发送给从节点, 保证主从复制进入正常状态。 (同步了 offset 的部分数据, 所以部分复制的基础就是偏移量offset)
7. 心跳
主节点在建立成功后会维护这长连接彼此发送心跳检测
- 主从节点彼此都有心跳检测机制, 各自模拟成对方的客户端进行通信, 通过client list命令查看复制相关客户端信息, 主节点的连接状态为flags=M,从节点连接状flags=S
- 主节点默认每隔10秒对从节点发送ping命令, 判断从节点的存活性和连接状态。 可通过参数repl-ping-slave-period控制发送频率。
- 从节点在主线程中每隔1秒发送replconf ack {offset} 命令, 给主节点上报自身当前的复制偏移量。
8. 缓冲大小调节
由于缓冲区长度固定且有限, 因此可以备份的写命令也有限, 当主从节点offset的差距过大超过缓冲区长度时, 将无法执行部分复制, 只能执行全量复制。 反过来说, 为了提高网络中断时部分复制执行的概率, 可以根据需要增大复制积压缓冲区的大小(通过配置repl-backlog-size)来设置; 例如 如果网络中断的平均时间是60s, 而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB, 则复制积压缓冲区的平均需求为6MB, 保险起见, 可以设置为12MB, 来保证绝大多数断线情况都可以使用部分复制。
参考:
六星教育的文档
http://www.elecfans.com/d/883975.html


浙公网安备 33010602011771号