本文共 6704 字,大约阅读时间需要 22 分钟。
,之前在这篇文章中已经搭建好主从复制的环境了,搭建起来非常简单,接下来就要搞清楚主从复制的原理了。
当一个 master 实例和一个 slave 实例连接正常时, master 会发送一连串的命令流来保持对 slave 的更新,以便于将自身数据集的改变复制给 slave :包括客户端的写入、key 的过期或被逐出等等。
当 master 和 slave 之间的连接断开之后,因为网络问题、或者是主从意识到连接超时, slave 重新连接上 master 并会尝试进行部分重同步:这意味着它会尝试只获取在断开连接期间内丢失的命令流。
当无法进行部分重同步时, slave 会请求进行全量重同步。这会涉及到一个更复杂的过程,例如 master 需要创建所有数据的快照,将之发送给 slave ,之后在数据集更改时持续发送命令流到 slave 。
当从节点启动后,每秒会尝试一次通过配置的IP和端口与主节点建立连接。
主节点启动后
从服务
当连接建立完成后,就会进入初始数据同步阶段,根据上图日志输出内容可以看出主从节点分别又有如下几步过程:
从节点
MASTER <-> REPLICA sync started 主从同步开始Non blocking connect for SYNC fired the event. 触发同步非阻塞连接事件,表明接下来的同步阶段,从服务可以正常接收客户端的查询请求。Master replied to PING, replication can continue... 通过ping确认主从之间通信正常Partial resynchronization not possible (no cached master) PSYNC模式,部分复制,主节点不存在replication ID,所有无法进行部分复制Full resync from master: ad7c41329ea01cdf36e034598584357492bc3c76:0 全量复制,并从主节点那获取到replication ID,这样下次同步就可以进行PSYNC
当数据同步完成后,接下来就是清除旧数据,把新数据加载到内存,并且如果开启了AOF,同步还会把同步到的数据写入到AOF文件中。
注意从节点最终在删除旧数据(4.0开始清除旧数据可以在另外一个线程中进行),加载新数据的过程中,依然会阻塞所有客户端请求。
主节点
Replica 127.0.0.1:6380 asks for synchronization 收到从节点的同步请求Full resync requested by replica 127.0.0.1:6380 采用全量同步Starting BGSAVE for SYNC with target: disk 使用bgsave的方式获取全量数据Background saving started by pid 6280 后台fork一个子进程,完成bgsave任务。
主节点在整个过程中都是非阻塞的
主节点将写命令异步的方式发送给从节点,并不会等从节点返回,因此会存在主从不一致的情况。
repl-disable-tcp-nodelay ,默认为no,主节点的数据会立刻同步给从节点,数据不一致的情况较少,如果设置为yes,主节点的数据并不会立刻就同步给从节点,而是会将数据打包合并,然后默认为40ms一次(linux内核的默认配置)同步给从节点,节省了网络io消耗,但也增加了数据不一致的时间。
关于此参数,官方也给出了适当的建议:
By default we optimize for low latency, but in very high traffic conditions
or when the master and replicas are many hops away, turning this to “yes” may be a good idea.从服务和主服务的数据同步可以分为两种情况:
对于初次复制来说,毫无疑问只能使用全量同步的方式,但毕竟全量同步的效率太低,所以对于断线后重连复制这种场景就没有必要再进行全量同步。
全量同步的流程
1、主节点通过bgsave命令,生成RDB文件,同时使用一个缓冲区用来记录此时客户端的写命令。
2、RDB文件创建完成后,发送给从节点。 3、从节点清除旧数据,载入新的RDB文件(载入过程会造成从节点客户端阻塞)。 4、从节点RDB文件载入完成后,从节点正常接收客户端的请求。 5、主节点把缓冲区中的数据发送给从节点,从节点载入缓冲区数据。 6、主从节点进入命令传播阶段,正常同步数据。全量同步的缺点
由于全量同步是使用RDB的方式实现的,那么就会带来如下一些问题:
1、当主服务器执行bgsave生成RDB文件时,会消耗主服务器大量的CPU、内存和磁盘IO资源。
2、主服务器把全量的RDB文件发送给从服务器,这个过程会消费主从服务器大量的网络资源,并对主服务器响应命令请求的时间产生影响。
3、从服务器接收RDB文件后,需要删除旧的RDB,加载新的RDB,在此过程期间从服务器会进入阻塞状态(4.0开始删除可以在另外的线程中执行,但是加载依然会阻塞。)
部分同步
为了优化断线后复制的问题,Redis从2.8版本开始,使用PSYNC命令代替SYNC命令来执行复制时的同步操作,PSYNC命令可以实现在断线后复制的场景中,不使用全量同步依旧可以完成主从数据同步的功能。
注意要实现部分同步功能,从节点必须关闭AOF功能。
每一个 Redis master 都有一个 replication ID(相当于4.0版本之前的 run ID) :这是一个较大的伪随机字符串,标记了一个给定的数据集。每个 master 也持有一个偏移量,master 将自己产生的复制流发送给 slave 时,发送多少个字节的数据,自身的偏移量就会增加多少,目的是当有新的操作修改自己的数据集时,它可以以此更新 slave 的状态。复制偏移量即使在没有一个 slave 连接到 master 时,也会自增,所以基本上每一对给定的Replication ID:offset都会标识一个 master 数据集的确切版本。
当 slave 连接到 master 时,它们使用 PSYNC 命令来发送它们记录的旧的 master replication ID 和它们至今为止处理的偏移量。通过这种方式, master 能够仅发送 slave 所需的增量部分。但是如果 master 的缓冲区中没有足够的命令积压缓冲记录,或者如果 slave 引用了不再知道的历史记录(replication ID),则会转而进行一个全量重同步:在这种情况下, slave 会得到一个完整的数据集副本,从头开始。
从节点断线后重启后部分同步
6371:S 04 Nov 2020 08:18:35.394 * Trying a partial resynchronization (request ad7c41329ea01cdf36e034598584357492bc3c76:4583).6371:S 04 Nov 2020 08:18:35.394 * Successful partial resynchronization with master.6371:S 04 Nov 2020 08:18:35.394 * MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.
主节点日志信息
6265:M 04 Nov 2020 08:18:35.394 * Replica 127.0.0.1:6380 asks for synchronization6265:M 04 Nov 2020 08:18:35.394 * Partial resynchronization request from 127.0.0.1:6380 accepted. Sending 2338 bytes of backlog starting from offset 4583.
也可以通过连接客户端,执行info replication命令查询主从阶段的复制信息
role:slave
master_host:192.168.70.113 master_port:6379 master_link_status:up master_last_io_seconds_ago:1 master_sync_in_progress:0 slave_repl_offset:9686 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:ad7c41329ea01cdf36e034598584357492bc3c76 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:9686 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:4583 repl_backlog_histlen:5104积压缓冲区
由repl-backlog-size参数配置,默认大小为1m。
当从节点断开连接一段时间后,主节点会积累此时客户端请求的数据,由这部分数据就保存在积压缓冲区中,当从节点重新连接时,就不需要全量同步了,只需同步积压缓冲区中的数据即可。
# Set the replication backlog size. The backlog is a buffer that accumulates# replica data when replicas are disconnected for some time, so that when a replica# wants to reconnect again, often a full resync is not needed, but a partial# resync is enough, just passing the portion of data the replica missed while# disconnected.## The bigger the replication backlog, the longer the time the replica can be# disconnected and later be able to perform a partial resynchronization.## The backlog is only allocated once there is at least a replica connected.#repl-backlog-size 1mb
积压缓冲区大小建议
如果主服务器需要执行大量写命令,又或者主从服务器断线后重新连接所需要的时间比较长,那么默认1M的大小可能就不太合适,一旦写数据超出缓冲区大小,那么从服务断线重连后就只能进行全量同步。
所以一般建议此值设置为每秒产生的写数据*从服务断线后重新连接上主服务器所需要的平均时间。
例如主服务器每秒产生1M的写数据,而从服务器断线重连平均需要5秒,那么积压缓冲区的大小就不能低于5M。
Redis4.0版本时为了解决关机run ID丢失(4.0版本中被replication ID替代)、主节点切换这两个问题提供了新的方式。
1、当从节点关闭时,会把replication ID和offset保存到RDB文件中,这样replication ID就不会丢失。
2、从节点中保存了master_replid和master_replid2两份数据
master_replid表示当前主节点的id
master_replid2表示前一次主节点的id这样做的目的是为了能够在主节点发生变更时,其他从节点再向新的主节点同步时,可以不需要全量同步。
让当前从节点变为主节点,此时master_replid2就为之前master_replid的值了。
如果你配置了从节点也可以写数据,那么要注意了,Redis的主从复制仅仅是从主节点同步数据。
例如有如下配置:
A —> B —-> C
即使节点 B 是可写的,C 也不会看到 B 的写入,而是将拥有和 master 实例 A 相同的数据集。
当主从节点全量同步时,主节点会把RDB文件发送给从节点,此时客户端发来的请求,会被主节点写入到一个缓冲区中,也就是这里的客户端输出缓冲区,直接从节点加载完RDB文件,所以如果此时有大量的写命令到来,而从节点加载RDB文件时间又比较长,那么当这个缓冲区超过以下配置的大小时,就会断开主从节点的连接,然后从节点就会重新连接 - - - 接着又因为缓冲区超出大小 - - - 断开连接 - - - 重连。。。
主从复制 看第2个配置项,表示大小超过256m连接就会被关闭,或者当大小超过64m,并且持续了60秒,连接也会被关闭。
client-output-buffer-limit normal 0 0 0client-output-buffer-limit replica 256mb 64mb 60client-output-buffer-limit pubsub 32mb 8mb 60
积压缓冲区 VS 客户端输出缓冲区
注意这两个缓冲区的区别,积压缓冲区主节点只有一个,而主节点会为每一个从节点都分配一个客户端输出缓冲区。
1、积压缓冲区默认大小是1m,决定主从节点是否可以进行部分同步。
2、客户端输出缓冲区默认配置是client-output-buffer-limit replica 256mb 64mb 60,决定是否要断开主从节点的连接。转载地址:http://tllrb.baihongyu.com/