博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入分析Redis主从复制原理
阅读量:2489 次
发布时间:2019-05-11

本文共 6704 字,大约阅读时间需要 22 分钟。

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

主从复制的机制

  • 当一个 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 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: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 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/

你可能感兴趣的文章
php计算上个月是几月份
查看>>
浅谈 trie树 及其实现
查看>>
60款很酷的 jQuery 幻灯片演示和下载
查看>>
JavaScript数组内置排序函数
查看>>
hdu 3549 Flow Problem(最大流模板题)
查看>>
nyoj-20-吝啬的国度(深搜)
查看>>
Vue.js(2.x)之Class 与 Style 绑定
查看>>
属性“dataProvider”有多个初始值设定项。(注意:“dataProvider”是“mx.charts.BarChart”的默认属性)。...
查看>>
C3P0在多线程下的maxPoolSize配置
查看>>
宽客的人&amp;&amp;事件映射
查看>>
linux(fedora) 下dvwa 建筑环境
查看>>
Oracle 跨库 查询 复制表数据 分布式查询
查看>>
Python笔记(十五)_异常处理
查看>>
C\C++ 获取当前路径
查看>>
Pandas CookBook -- 04选取数据子集
查看>>
正则表达式语法
查看>>
算法导论第22章22.2广度优先搜索
查看>>
解决Pytohn安装第三方库出现read timed out 问题
查看>>
笔记整理——linux程序设计
查看>>
20144303 20145239 实验三
查看>>