Redis(九)主从复制
2022-09-22 22:46:27

目录
[TOC]

what? why? how?

什么是主从复制?

  • 主从?结构分为主服务器、从服务器

  • 复制?主服务器的数据,自动同步到从服务器

  • 图解

image.png
这是简单的一主二从结构

为什么需要主从复制?

需要新知识还是要先思考存在的原因,在进行学习

我认为原因主要有两点:

  • 负载均衡
    在高并发的情况,可以主服务器只进行写操作,而从服务器进行读操作(读写分离)【分担单个Redis的压力】
  • 高可用:
    假设只存在一个Redis服务器,那么它挂了会怎样,访问压力会直接落到数据库中,数据库表示压力山大(Redis用作缓存时)。
    若存在多个Redis服务器当一个挂掉了,就可以暂时由从服务器来替代挂的的Redis的工作(哨兵机制)

读写分离不是强制的,可以在Redis配置文件里进行配置。
目前的Redis版本默认的配置就是【主写,从读】

如何在本地搭建主从服务器?

原来

首先测试原来只有一台Redis服务器的情况,用Redis客户端连接,打印信息

先介绍一个命令 info replication : 查看主/从复制信息

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> info replication
# Replication
role:master # 角色是 主机
connected_slaves:0 # 没有从机
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

搭建

我是在Windows系统下进行搭建测试的,Linux系统区别不大

这里是配置成一主二从的结构

总览

  • 配从(库)不配主(库)
  • 从库配置:slaveof 主库IP 主库端口
  • info replication:查看主/从复制信息

具体

首先在本地模拟多个Redis服务器,先创建三个Redis的配置文件

image.png

主机的配置文件:redis.windows.conf

1
2
3
4
port 6379  #端口
dbfilename dump-6379.rdb #rdb 文件
logfile "6379.log" #日志文件 (这个可以不改,默认空白就将日志输出到命令行界面,若修改后,就保存在6379.log文件中,而命令行窗口没有提示日志)
(linux还要修改 # NOT SUPPORTED ON WINDOWS pidfile /var/run/redis.pid 比如为 6379.pid , 防止pid冲突。Windows不支持 )

从机的配置文件 redis.windows-1.confredis.windows-2.conf

1
2
3
4
5
6
7
8
9
10
11
#从机1
port 6380 #端口
dbfilename dump-6380.rdb #rdb 文件
logfile "6380.log" #日志文件 (这个可以不改,默认空白就将日志输出到命令行界面,若修改后,就保存在6381.log文件中,而命令行窗口没有提示日志)
(linux还要修改 # NOT SUPPORTED ON WINDOWS pidfile /var/run/redis.pid 比如为 6380.pid , 防止pid冲突。Windows不支持 )

#从机2
port 6381 #端口
dbfilename dump-6381.rdb #rdb 文件
logfile "6381.log" #日志文件 (这个可以不改,默认空白就将日志输出到命令行界面,若修改后,就保存在6381.log文件中,而命令行窗口没有提示日志)
(linux还要修改 # NOT SUPPORTED ON WINDOWS pidfile /var/run/redis.pid 比如为 6381.pid , 防止pid冲突。Windows不支持 )

然后分别利用这三个不同的配置文件来启动Redis服务器

image.png

然后在启动三个客户端来进行连接

image.png

这里可以看到:默认每个Redis服务器都是主机(Master),接下来进行主从(Master|Savle)的配置

我们来配置成一主二从的情况

image.png

其实关键的步骤非常简单呀,就是一个命令slaveof host port

只是从机执行slaveof就可以;1

1
2
3
4
5
#将6380、6381端口的服务器,都执行
# 1. slaveof 从机配置
slaveof 127.0.0.1 6379
# 2. 查看状态
info replication

image.png

主机再次查看状态

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> info replication
# Replication
role:master # 6379是主机
connected_slaves:2 # 有2个从机
# 从机信息
slave0:ip=127.0.0.1,port=6380,state=online,offset=1163,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=1163,lag=0
master_repl_offset:1163
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:1162

上面用命令输入只是临时的配置,Redis服务器Shutdown后主从关系就消失了

在项目的真实环境中,一般直接在配置文件里配置主从关系,才能保证主从关系一直存在

配置文件的话,在如下位置进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
################################# REPLICATION #################################

# Master-Slave replication. Use slaveof to make a Redis instance a copy of
# another Redis server. A few things to understand ASAP about Redis replication.
#
# 1) Redis replication is asynchronous, but you can configure a master to
# stop accepting writes if it appears to be not connected with at least
# a given number of slaves.
# 2) Redis slaves are able to perform a partial resynchronization with the
# master if the replication link is lost for a relatively small amount of
# time. You may want to configure the replication backlog size (see the next
# sections of this file) with a sensible value depending on your needs.
# 3) Replication is automatic and does not need user intervention. After a
# network partition slaves automatically try to reconnect to masters
# and resynchronize with them.
主机ip 端口
slaveof <masterip> <masterport>

# If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the slave to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the slave request.
主机密码
masterauth <master-password>
主机密码应该是,主从机都要进行配置的。
如果只是配置主机密码,而没有配置从机密码。
在执行slaveof的命令时会报错。【这里记不太清了,有待确定】

进行配置后(命令行或配置文件的方式都可以),会有以下效果(conf文件里的默认配置):

  • 主机可以写,从机只能读不能写
  • 主机中所有的信息和数据,都会自动同步到从机

接下来进行测试

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#下面的命令是在三个不同客户端输入的注意区分!
# ---------------------- 6379 【master】 ----------------------
127.0.0.1:6379> set feel happy # 读
OK
127.0.0.1:6379> get feel # 写, 也就是说主机,还是可读可写的
"happy"

# ---------------------- 6380 【slave】 ----------------------
127.0.0.1:6380> keys * # 读
1) "feel"
127.0.0.1:6380> get feel # 读
"happy"
127.0.0.1:6380> set message iamslave # 写,也就是说从机,是默认拒绝执行写操作的
(error) READONLY You can't write against a read only slave.

# ---------------------- 6381 【slave】 ----------------------
127.0.0.1:6381> get feel # 读
"happy"

主机挂了会怎样?

还是上面的配置

  • 6379 master
    • 6380 slave
    • 6381 slave
1
2
3
4
5
6
7
8
9
10
11
12
# 6379 端口 查看主从信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=337,lag=1
slave1:ip=127.0.0.1,port=6380,state=online,offset=337,lag=1
master_repl_offset:337
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:336

测试

这时我们手动让主机挂掉(shutdown)

我们主要测试下面的问题,测试前可以先猜一猜:

主机挂掉,会对主从结构有影响吗?

主机挂掉,从机是否变成能写入了?

主机挂掉,从机是否会自动变为主机?

1
2
3
# ----------------------    6379 【master】    ----------------------
127.0.0.1:6379> shutdown
not connected>

再来看看从机是否有变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# ----------------------    6380 【slave】    ----------------------
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:687
master_link_down_since_seconds:jd
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6380> keys * # 由于之前主机设置的值,被同步到从机了
1) "feel" # 所以这里依然能获取到
127.0.0.1:6380> set k8 v8 #还是不能写
(error) READONLY You can't write against a read only slave.

# ---------------------- 6381 【slave】 ----------------------
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:687
master_link_down_since_seconds:jd
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

我们发现从机还是slave并没有变化。

然后我们再次启动原来的主机6379

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
PS F:\...\Redis-x64-3.2.100> .\redis-server.exe .\redis.windows.conf
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 3.2.100 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 7408
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'

[7408] 30 Oct 06:20:16.246 # Server started, Redis version 3.2.100
[7408] 30 Oct 06:20:16.262 * DB loaded from disk: 0.000 seconds
[7408] 30 Oct 06:20:16.262 * The server is now ready to accept connections on port 6379
[7408] 30 Oct 06:20:16.697 * Slave 127.0.0.1:6380 asks for synchronization
[7408] 30 Oct 06:20:16.697 * Partial resynchronization not accepted: Runid mismatch (Client asked for runid '816bd2f39dd9de61fea057b9ae893a99bd36c8de', my runid is 'ce0d9f7a7dc2a429491c13beefb946c12ae949e4')
[7408] 30 Oct 06:20:16.697 * Starting BGSAVE for SYNC with target: disk
[7408] 30 Oct 06:20:16.697 * Background saving started by pid 1692
[7408] 30 Oct 06:20:16.734 * Slave 127.0.0.1:6381 asks for synchronization
[7408] 30 Oct 06:20:16.734 * Partial resynchronization not accepted: Runid mismatch (Client asked for runid '816bd2f39dd9de61fea057b9ae893a99bd36c8de', my runid is 'ce0d9f7a7dc2a429491c13beefb946c12ae949e4')
[7408] 30 Oct 06:20:16.734 * Waiting for end of BGSAVE for SYNC
[7408] 30 Oct 06:20:17.097 # fork operation complete
[7408] 30 Oct 06:20:17.097 * Background saving terminated with success
# 两个从机直接连接上了
[7408] 30 Oct 06:20:17.097 * Synchronization with slave 127.0.0.1:6380 succeeded
[7408] 30 Oct 06:20:17.097 * Synchronization with slave 127.0.0.1:6381 succeeded

然后再次往主机上设置值,测试从机是否能get到

1
2
3
4
5
6
# ----------------------    6379 【master】    ----------------------
127.0.0.1:6379> set message iamBack
OK
# ---------------------- 6380 【slave】 -----------------------
127.0.0.1:6380> get message
"iamBack"

总结

  • 主机断开,从机依旧连接到到主机,但是没有写操作

  • 主机如果回来了,从机依旧可以获取到主机写的信息

这里主机断了,虽然重连后可以正常使用,但在断连的一段时间里都无法进行写操作(程序这一段时间无法正常写入缓存,这不是期望的结果)。

我们在实际中期望的状态是:主机断了,还是能接着进行写操作(可以在两个从机里选出一个来担任主机,来继续运行下去)==> 这可以通过哨兵实现,在后面文章会介绍

从机挂了会怎样?

测试

我们主要测试下面的问题,测试前可以先猜一猜

从机挂了,会对主从结构有影响吗?

从机断连的时间段,主机写入的数据,从机重连后还能获取到吗?

测试开始:手动让6380的从机挂掉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ----------------------    6380 【slave】    ----------------------

127.0.0.1:6380> shutdown # 6380 GG
not connected>

# ---------------------- 6379 【master】 ----------------------

127.0.0.1:6379> set mes 6380canyouGet? # 主机set了一个值
OK

# ---------------------- 6381 【slave】 ----------------------

127.0.0.1:6381> get mes # 6381 还是从机肯能获取到
"6380canyouGet?"

6380重连

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
PS F:\...\Redis-x64-3.2.100> .\redis-server.exe .\redis.windows-1.conf
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 3.2.100 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6380
| `-._ `._ / _.-' | PID: 12036
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'

[12036] 30 Oct 06:46:59.117 # Server started, Redis version 3.2.100
[12036] 30 Oct 06:46:59.117 * DB loaded from disk: 0.000 seconds
[12036] 30 Oct 06:46:59.117 * The server is now ready to accept connections on port 6380

# ---------------------- 6380 【slave】 ----------------------

PS F:\CodeSoft\Redis-x64-3.2.100> .\redis-cli.exe -p 6380
127.0.0.1:6380> get mes # 获取不到,断连时,主机set的数据
(nil)

127.0.0.1:6380> keys * # 重连后的数据状态 还是 断连时的数据状态
1) "message"
2) "feel"

127.0.0.1:6380> info replication # 在看看主从信息
# Replication
role:master #之前命令行配置的主从失效了,6380默认根据配置文件变成了主机
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

127.0.0.1:6380> slaveof 127.0.0.1 6379 # 再把它变为6379的从机
OK
127.0.0.1:6380> get mes # 自动进行了主从数据复制 所以又可以获取主机数据了
"6380canyouGet?"
127.0.0.1:6380> keys *
1) "feel"
2) "mes"
3) "message"

这里要注意一下:之前都是通过命令行输入slaveof 进行主从配置测试的。(而不是配置文件)

总结

  • 命令行配置的主从信息,再重连后会失效
  • 配置文件配置的则不会失效,也就是说上面6380如果是里配置文件进行配置的,那么重连后依旧是6379的从机可以获取断连时主机的set的数据

一带一路(链路结构)

上面介绍了的一主二从的分支结构,而实际项目中Redis主从结构可能是各种样式的

比如一主2从,然后每一个从服务器后面还可能跟着一个服务器等等..

下面这张图是三台Redis的一条链路的主从结构(可以戏称为一带一路…):

image.png

Master节点不做变化,slave-1执行:slaveof 这个master,slave-2执行:slaveof 这个slave-1就配置成这种结构了。具体命令如上面介绍的

中间的节点还是Slave,但是后面还可以接多个Slave。

  • 主从复制还是存在的。比如最后的一个Slave可以获取到Master中set的数据,这里不做演示了
  • 加入Matser断开连接,后面的Slave主从节点结构不变

谋朝篡位(变为Master节点)

接着上面的一带一路来说,假如Matser断开连接了,那么下面的Slave-1或Slave-2就在想如何当Master呢?

其实命令很简单:slaveof no one (翻译一下就是:不做任何人的奴隶,所以就成为主人了【Master】)

这里是相当于手动地升级为Master,再实际中一般由哨兵来完成。哨兵的讲解在下一篇文章

我们来分析Master宕机时,Slave节点谋朝篡位(升级Master)后的主从结构变化:

情况一:当Slave还没谋朝篡位时:Master重连的话,主从结构不变

情况二:当Slave-1升级Master后:由于Slave-2本来就是slave of Slave-1的。所以Slave2还是slave-1的从机。而这是加入Master重连成功,解决还是Master。只不过是一个孤零零的Matser,没有从机了

image.png

情况二:当Slave-2升级Master后,Slave-2不再是任何主机的从机,也没有自己的Slave节点

image-20201030194256097

如何复制?

原理

从上图可以看出来大致分为6个过程:

  • 执行slaveof后从节点保存主节点的地址信息便返回,这时候复制流程还没开始。
  • 从节点内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接,从节点会建立一个socket套接字。
  • 发送ping命令,检测主从之间网络套接字是否可用,检测主节点是否可用接受处理命令。如果发送 ping 命令后,从节点没有收到主节点的 pong 回复或者超时,比如网络超时或者主节点正在阻塞无法响应命令,从节点会断开复制连接,下次定时任务会发起重连。
  • 如果主节点配置了requirepass参数,则需要密码认证,从节点必须配置masterauth参数保证与主节点相同的密码才能通过验证。
  • 主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤。
  • 当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来主节点会持续地把写命令发送给从节点,保证主从数据一致性。

主从同步的过程中,从节点会把原来的数据清空。

数据同步

同步方式:

  • 全量复制

    用于初次复制或其它无法进行部分复制的情况,将主节点中的所有数据都发送给从节点。当数据量过大的时候,会造成很大的网络开销。

  • 部分复制

    用于处理在主从复制中因网络闪退等原因造成数据丢失场景,当从节点再次连上主节点,如果条件允许,主节点会补发丢失数据给从节点,因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销。但需要注意,如果网络中断时间过长,造成主节点没有能够完整地保存中断期间执行的写命令,则无法进行部分复制,仍使用全量复制 。

复制偏移量:

  • 参与复制的主从节点都会维护自身复制偏移量,主节点在处理完写入命令操作后,会把命令的字节长度做累加记录,统计信息在info replication中的master_repl_offset指标中。
  • 从节点每秒钟上报自身的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量slave0:ip=192.168.1.3,port=6379,state=online,offset=116424,lag=0
  • 从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在info replication中的slave_repl_offset中。

复制积压缓冲区:

  • 复制积压缓冲区是保存在主节点上的一个固定长度的队列,默认大小为1MB,当主节点有连接的从节点时被创建,这时主节点响应写命令时,不但会把命令发给从节点,还会写入复制积压缓冲区。
  • 在命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中 的每个字节对应的复制偏移量(offset) 。由于复制积压缓冲区定长且先进先出,所以它保存的是主节点最近执行的写命令;时间较早的写命令会被挤出缓冲区。

主节点运行ID:

  • 每个redis节点启动后都会动态分配一个40位的十六进制字符串为运行ID。运行ID的主要作用是来唯一识别redis节点,比如从节点保存主节点的运行ID识别自已正在复制是哪个主节点。如果只使用ip+port的方式识别主节点,那么主节点重启变更了整体数据集(如替换RDB/AOF文件),从节点再基于偏移量复制数据将是不安全的,因此当运行ID变化后从节点将做全量复制。可以在info server命令查看当前节点的运行ID。
  • 需要注意的是redis关闭再启动,运行的id会随之变化。

Psync命令:

img

  • 从节点使用psync命令完成部分复制和全量复制功能psync runid offset

  • 流程说明:

    • 从节点(slave)发送psync命令给主节点,参数runid是当前从节点保存的主节点运行id,如果没有则默认值为 ?, 参数offset是当前从节点保存的复制偏移量,如果是第一次参与复制则默认值为-1。

    • 主节点根据

      1
      pysnc

      参数和自身数据情况决定响应结果:

      • 如果回复+FULLRESYNC {runid} {offset},那么从节点将触发全量复制流程。
      • 如果回复+CONTINUE,从节点将触发部分复制流程。
      • 如果回复-ERR,说明主节点版本低于Redis2.8。

全量复制流程:

img

  • 发送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 文件给从机)
    • 注意!对于数据量较大的主节点,比如生成的RDB文件超过6GB以上时要格外小心。传输文件这一步操作非常耗时,速度取决于主从节点之间网络带宽。
    • 通过细致分析Full resync和MASTER <-> SLAVE这两行日志的时间差,可以算出RDB文件从创建到传输完毕消耗的总时间。如果总时间超过repl-timeout所配置的值 (默认60秒),从节点将放弃接受RDB文件并清理已经下载的临时文件,导致全量复制失败;针对数据量较大的节点,建议调大repl-timeout参数防止出现全量同步数据超时;
    • 例如对于千兆网卡的机器,网卡带宽理论峰值大约每秒传输100MB,在不考虑其他进程消耗带宽的情况下,6GB的RDB文件至少需要60秒传输时间,默认配置下,极易出现主从数同步超时。
  • 对于从节点开始接收RDB快照到接收完成期间,主节点仍然响应读写命令,因此主节点会把这期间写命令数据保存在复制客户端缓冲区内,当从节点加载完RDB文件后,主节点再把缓冲区内的数据发送给从节点,保证主从之间数据致性。(发送缓冲区数据)
  • 从节点接收完主节点传送来的全部数据后会清空自身旧数据(刷新旧的数据,从节点在载入主节点的数据之前要先将老数据清除)
  • 从节点清空数据后开始加载RDB文件,对于较大的RDB文件,这一步操作依然比较消耗时间,可以通过计算日志之间的实际差来判断加载RDB的总消耗时间(加载 RDB 文件将数据库状态更新至主节点执行bgsave时的数据库状态和缓冲区数据的加载。)
  • 从节点成功加载完RDB后,如果当前节点开启了AOF持久化的功能,它会立刻做bgrewriteeaof的操作,为了保证全量复制后AOF持久化文件立刻可用。 通过分析全量复制的所有流程,全量复制是一个非常耗时费力的操作。他的实际开销主要包括:
    • 主节点bgsave时间
    • RDB文件网络传输时间
    • 从节点清空数据时间
    • 从节点加载RDB的时间
    • 可能的AOF重写时间

部分复制流程:

img

  • 部分复制是 Redis 2.8 以后出现的,之所以要加入部分复制,是因为全量复制会产生很多问题,比如像上面的时间开销大、无法隔离等问题, Redis 希望能够在主节点出现抖动(相当于断开连接)的时候,可以有一些机制将复制的损失降低到最低
  • 当主从节点之间网络出现中断时,如果超过repl-timeout时间,主节点会认为从节点出问题了并断开复制链接(如果网络抖动(连接断开 connection lost))。
  • 主从连接中断期间主节点依然响应命令,但因复制链接中断命令无法发送给从节点不过主节点内部存在的复制积压缓存去,依然可以保存一段时间的写命令数据,默认最大缓存1MB(主机master 还是会写 replbackbuffer(复制缓冲区))
  • 当主从节点网络恢复后,从节点会再次连上主节点。(从机slave会继续尝试连接主机)
  • 当主从连接恢复后,由于从节点之前保存了自身已复制的偏移量和主节点的运行id。因此会把他们当作psync参数发送给主节点,要求进行部分复制操作。(从机 slave 会把自己当前 runid 和偏移量传输给主机 master,并且执行 pysnc 命令同步)
  • 主节点接到psync命令后首先核对参数的runid,如果 master 发现你的偏移量是在缓冲区的范围内,根据参数offset在缓冲区查找复制内内,如果在偏移量之后的数据存在缓存区中,则对从节点发送continue表示可以进行部分复制
  • 主节点根据偏移量把复制积压缓冲区里的数据发送给从节点,保证主从复制进入正常状态。(同步了 offset 的部分数据,所以部分复制的基础就是偏移量 offset)

心跳:

主节点在建立成功后会维护这长连接彼此发送心跳检测

  • 主从节点彼此都有心跳检测机制,各自模拟成对方的客户端进行通信,通过client list命令查看复制相关客户端信息,主节点的连接状态为flags=M,从节点连接状态 flags=S。
  • 主节点默认每隔10秒对从节点发送ping命令,判断从节点的存活性和连接状态。可通过参数repl-ping-slave-period控制发送频率。
  • 从节点在主线程中每隔1秒发送replconf ack {offset} 命令,给主节点上报自身当前的复制偏移量。

缓冲区大小调节:

  • 由于缓冲区长度固定且有限,因此可以备份的写命令也有限,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。
  • 反过来说,为了提高网络中断时部分复制执行的概率,可以根据需要增大复制积压缓冲区的大小(通过配置repl-backlog-size)来设置;
  • 例如 如果网络中断的平均时间是 60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见, 可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制。

未完待续…


Prev
2022-09-22 22:46:27
Next