深入分析Redis主从复制原理

Redis主从环境详细搭建过程,之前在这篇文章中已经搭建好主从复制的环境了,搭建起来非常简单,接下来就要搞清楚主从复制的原理了。

主从复制的机制

  • 当一个 master 实例和一个 slave 实例连接正常时, master 会发送一连串的命令流来保持对 slave 的更新,以便于将自身数据集的改变复制给 slave :包括客户端的写入、key 的过期或被逐出等等。

  • 当 master 和 slave 之间的连接断开之后,因为网络问题、或者是主从意识到连接超时, slave 重新连接上 master 并会尝试进行部分重同步:这意味着它会尝试只获取在断开连接期间内丢失的命令流。

  • 当无法进行部分重同步时, slave 会请求进行全量重同步。这会涉及到一个更复杂的过程,例如 master 需要创建所有数据的快照,将之发送给 slave ,之后在数据集更改时持续发送命令流到 slave 。

主从复制的三大阶段

1、连接建立阶段

当从节点启动后,每秒会尝试一次通过配置的IP和端口与主节点建立连接。

在这里插入图片描述

主节点启动后

在这里插入图片描述

从服务

在这里插入图片描述

2、初始数据同步阶段

当连接建立完成后,就会进入初始数据同步阶段,根据上图日志输出内容可以看出主从节点分别又有如下几步过程:

从节点

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任务。

主节点在整个过程中都是非阻塞的

3、命令传播阶段

主节点将写命令异步的方式发送给从节点,并不会等从节点返回,因此会存在主从不一致的情况。

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.

全量同步 VS 部分同步

从服务和主服务的数据同步可以分为两种情况:

  • 初次复制:从服务器以前从来没有复制过任何主服务器数据,或者从服务器当前要复制的主服务器和上一次复制的主服务器不同。
  • 断线后复制:处于命令传播阶段的从服务器因为网络等原因中断了复制过程,但从服务器通过重新连接又连接上了主服务器,并继续复制主服务器数据。

对于初次复制来说,毫无疑问只能使用全量同步的方式,但毕竟全量同步的效率太低,所以对于断线后重连复制这种场景就没有必要再进行全量同步。

全量同步的流程

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功能。

PSYNC的具体实现

每一个 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 synchronization
6265: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:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=9686,lag=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:1
repl_backlog_histlen:9686

  • 从节点的复制信息

在这里插入图片描述

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。

部分同步失败原因

  • 从节点保存的replication ID丢失。
  • 主节点更换,导致从节点保存的replication ID与当前的主节点不一致。
  • 主节点的写数据超过积压缓冲区大小。
  • 设置了缓冲区数据的失效时间(当没有从节点连接到主节点时,超过repl-backlog-ttl参数时间,主节点就会清楚缓冲区数据,默认没有时间限制)。
    如果超过失效时间主节点会输出:
    Replication backlog freed after 10 seconds without connected replicas。

4.0版本PSYNC2

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 相同的数据集。

Redis无限全量同步问题

当主从节点全量同步时,主节点会把RDB文件发送给从节点,此时客户端发来的请求,会被主节点写入到一个缓冲区中,也就是这里的客户端输出缓冲区,直接从节点加载完RDB文件,所以如果此时有大量的写命令到来,而从节点加载RDB文件时间又比较长,那么当这个缓冲区超过以下配置的大小时,就会断开主从节点的连接,然后从节点就会重新连接 - - - 接着又因为缓冲区超出大小 - - - 断开连接 - - - 重连。。。

主从复制 看第2个配置项,表示大小超过256m连接就会被关闭,或者当大小超过64m,并且持续了60秒,连接也会被关闭。

client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

积压缓冲区 VS 客户端输出缓冲区

注意这两个缓冲区的区别,积压缓冲区主节点只有一个,而主节点会为每一个从节点都分配一个客户端输出缓冲区。

1、积压缓冲区默认大小是1m,决定主从节点是否可以进行部分同步。
2、客户端输出缓冲区默认配置是client-output-buffer-limit replica 256mb 64mb 60,决定是否要断开主从节点的连接。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码拉松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值