您当前的位置:首页 > 计算机 > 软件应用 > 数据库 > Redis

Redis主从复制的搭建及原理分析

时间:09-14来源:作者:点击数:

一、Redis主从复制

1.1 搭建主从复制架构

1.1.1 主从复制架构简介

在实际开发中,redis通常会搭建集群,来提高redis的整体的性能。但在客户端访问时有可能多次访问到不同的redis,因此造成多台redis数据不一致问题,为了解决这种多台redis中数据不同步问题,我们提出了主、从的概念;

image

Master负责写的操作,Slave负责读的操作,Master与Slave直接保证数据的同步。

注:一个Master可以对应有多个Slave,一个Slave只能有一个Master

1.1.2 搭建主从复制架构

准备好两个Redis配置文件(一主一从):

6379(Master):

port 6379
dir "/root/redis-4.0.11/data"
daemonize no
dbfilename dump-6379.rdb
appendonly yes
appendfsync everysec
appendfilename "appendonly-6379.aof"

6380(Slave):

port 6380
dir "/root/redis-4.0.11/data"
dbfilename dump-6380.rdb
daemonize no
appendonly yes
appendfsync everysec
appendfilename "appendonly-6380.aof"

启动两台Redis服务器

redis-4.0.11/src/redis-server redis-4.0.11/conf/redis-6379.conf
redis-4.0.11/src/redis-server redis-4.0.11/conf/redis-6380.conf

使用客户端连接:

redis-4.0.11/src/redis-cli -p 6379
redis-4.0.11/src/redis-cli -p 6380
  • 主从复制命令:

Slave连接Master,在Slave的客户端输入如下命令:

Slaveof <Masterip> <Masterport>

示例:

127.0.0.1:6379> Slaveof 192.168.170.142 6379
OK
127.0.0.1:6379>

如果Master设置了密码,那么在Slave服务器启动的时候就要指定Master的密码:

redis-server ../config/redis-6380.conf --Masterauth admin

分别查看Slave与Master启动日志:

  • Master日志:
23305:M 11 Apr 10:24:20.225 * Slave 127.0.0.1:6479 asks for synchronization
23305:M 11 Apr 10:24:20.225 * Partial resynchronization not accepted: Replication ID mismatch (Slave asked for '18b61ced5990337dd8377e8bf81fcd2b20e7d399', my replication IDs are 'fa4d40f8562c10acbbd7a9955a88f7cd6dc33182' and '0000000000000000000000000000000000000000')
23305:M 11 Apr 10:24:20.225 * Starting BGSAVE for SYNC with target: disk
23305:M 11 Apr 10:24:20.225 * Background saving started by pid 23339
23339:C 11 Apr 10:24:20.227 * DB saved on disk
23339:C 11 Apr 10:24:20.227 * RDB: 2 MB of memory used by copy-on-write
23305:M 11 Apr 10:24:20.315 * Background saving terminated with success
23305:M 11 Apr 10:24:20.315 * Synchronization with Slave 127.0.0.1:6479 succeeded
  • Slave日志:
23279:S 11 Apr 10:24:20.224 * Connecting to Master 127.0.0.1:6379
23279:S 11 Apr 10:24:20.224 * Master <-> Slave sync started
23279:S 11 Apr 10:24:20.224 * Non blocking connect for SYNC fired the event.
23279:S 11 Apr 10:24:20.224 * Master replied to PING, replication can continue...
23279:S 11 Apr 10:24:20.224 * Trying a partial resynchronization (request 18b61ced5990337dd8377e8bf81fcd2b20e7d399:1).
23279:S 11 Apr 10:24:20.226 * Full resync from Master: f1d39130cfadf4b3e0eb192e4143ae6bcb01677b:0
23279:S 11 Apr 10:24:20.226 * Discarding previously cached Master state.
23279:S 11 Apr 10:24:20.315 * Master <-> Slave sync: receiving 176 bytes from Master
23279:S 11 Apr 10:24:20.315 * Master <-> Slave sync: Flushing old data
23279:S 11 Apr 10:24:20.315 * Master <-> Slave sync: Loading DB in memory
23279:S 11 Apr 10:24:20.316 * Master <-> Slave sync: Finished with success
23279:S 11 Apr 10:24:20.316 * Background append only file rewriting started by pid 23340
23279:S 11 Apr 10:24:20.356 * AOF rewrite child asks to stop sending diffs.
23340:C 11 Apr 10:24:20.356 * Parent agreed to stop sending diffs. Finalizing AOF...
23340:C 11 Apr 10:24:20.356 * Concatenating 0.00 MB of AOF diff received from parent.
23340:C 11 Apr 10:24:20.356 * SYNC append only file rewrite performed
23340:C 11 Apr 10:24:20.357 * AOF rewrite: 6 MB of memory used by copy-on-write
23279:S 11 Apr 10:24:20.426 * Background AOF rewrite terminated with success
23279:S 11 Apr 10:24:20.426 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB)
23279:S 11 Apr 10:24:20.426 * Background AOF rewrite finished successfully
  • 当Master宕机后Slave会一直尝试连接Master,日志信息如下:
29511:S 11 Apr 10:37:37.567 # Error condition on socket for SYNC: Connection refused
29511:S 11 Apr 10:37:38.577 * Connecting to Master 127.0.0.1:6379
29511:S 11 Apr 10:37:38.577 * Master <-> Slave sync started
29511:S 11 Apr 10:37:38.577 # Error condition on socket for SYNC: Connection refused
29511:S 11 Apr 10:37:39.587 * Connecting to Master 127.0.0.1:6379
29511:S 11 Apr 10:37:39.587 * Master <-> Slave sync started
29511:S 11 Apr 10:37:39.587 # Error condition on socket for SYNC: Connection refused
29511:S 11 Apr 10:37:40.597 * Connecting to Master 127.0.0.1:6379
29511:S 11 Apr 10:37:40.597 * Master <-> Slave sync started
29511:S 11 Apr 10:37:40.597 # Error condition on socket for SYNC: Connection refused
29511:S 11 Apr 10:37:41.606 * Connecting to Master 127.0.0.1:6379

Slave端口与Master的连接(断开与Master连接后,Slave不再接收Master的同步数据):

Slaveof no one

1.2 主从复制工作流程

image

redis的主从复制分为三个阶段:

1)Slave连接Master(建立连接阶段)

2)Master同步数据到Slave(数据同步阶段)

3)期间Master接到来自客户端"写"的命令之后需要将数据同步到Slave(命令传播阶段)

1.2.1 建立连接阶段

在主从配置的建立连接阶段Master与Slave之间会做如下操作:

1)Slave发送Slaveof Masterhost Masterport命令连接Master

2)Master接到来自Slave的连接,并开始响应对方。

3)Slave得到响应之后将Masterhost与Masterport及一些其他的Master信息保存到Slave端。

4)Slave确保连接无误后开始创建socket通道,用于后续的数据复制工作。

5)Slave与Master之间周期性的发送ping心跳,检查Slave与Master之间是否通信正常。

6)Master接收到Slave的ping心跳后会给对应的Slave响应pong。ping/pong机制

7)Slave发送本机设置的Master密码Masterauth来到Master进行验证(Master有设置密码的情况下)。

8)Master进行身份识别,如果认证错误,尝试重新连接。Slave服务器端报如下错误:

Master aborted replication with an error: NOAUTH Authentication required.

9)Master身份识别成功后,Slave会将自己的ip、端口等信息发送到Master,Master将保存此Slave的ip、端口以及一些其他状态信息,记录在info Replication。

1.2.2 数据同步阶段
1.2.2.1 工作流程
image

在Master与Slave建立连接成功后,开始数据同步。

1)Slave发送psync2(psync1、sync)指令给Master,需要同步数据

2)Master接到指令后开始执行bgsave指令,并创建复制积压缓冲区,在此期间,Master接收到任何来自客户端的"写"操作都会记录在复制积压缓冲区一份

3)Master将rdb文件通过前面创建的socket通道发送给Slave

4)Slave接收到rdb文件后,清空当前机器的所有数据,开始同步rdb中的数据

5)告知Master文件以及恢复完毕

6)Master将复制积压缓冲区中的指令发送给Slave

7)Slave将接收到的指令执行bgrewriteaof重写,之后进行数据恢复

步骤1-4属于全量同步

步骤5-7属于增量同步

1.2.2.2 增量同步原理

从上图可以看出,在Master执行bgsave期间接收到的所有命令都会存放在复制积压缓冲区一份,而复制积压缓冲区的大小是有限度的,默认是1MB,如果缓冲区已经满了,则会把最前面的数据挤出去(删除),后期进行增量同步时发现数据不一致,则会进行全量同步,从而造成同步死循环,因此复制积压缓冲区不易设置为太小。

  • 查看复制积压缓冲区:
127.0.0.1:6379> config get repl-backlog-size
1) "repl-backlog-size"
2) "1048576"
127.0.0.1:6379>
  • 调整复制积压缓冲区大小:
repl-backlog-size 2096576
1.2.3 命令传播阶段

Master与Slave保持连接后,此后Master接到来自客户端"写"的命令之后,需要将数据同步各个Slave端,此阶段叫命令传播阶段。

1.2.3.1 偏移量(offset)

在数据同步中,Master与Slave之间分别维护着一个offset偏移量

  • Master的offset记录的是:Master在发送数据到Slave时Master已经发送的数据位置
  • Slave的offset记录的是:Slave实际接收到的数据位置

这样利于在网络抖动情况下,主从节点之间还可以继续接着上一次同步的位置进行同步,而不必要进行全量同步。

Tips:Master与Slave的offset不一致一般情况下是Master发送了指令但由于网络抖动等原因Slave没有接收到;

image

假设在命令传播时,Master已经传输到了"4"这个字节(offset为7),但Slave实际直接收到"t"这个字节(offset为5),由于Slave与Master一直保持着ping/pong机制,因此每秒Slave都会将自己所保存的offset发送到Master与之对比,那么下次Master则会从"t"这个字节开始发送数据到Slave;

1.2.3.2 运行id(runid)

在上一小结说了Master与Slave之间都维护着一个offset偏移量,让我们可以根据offset的偏移量进行增量同步,避免网络抖动情况下进行全量同步。

但是如果在同步的过程中切换了Master节点则会出现问题(哨兵切换等问题),因为新的Master节点并没有维护着offset偏移量,并且Slave中的数据应该与新Master节点数据保持一致。那么redis是如何做到这一点的呢?

image

其实在Master与Slave服务器启动时都保存有一个由40位随机的16进制字符串组成的运行id(runid),用于标识一台唯一的redis,每次启动都不一样。在info的server组下可以查看到:

info server
image

在Slave首次连接Master时,Master会将自己的runid发送给Slave,Slave会将此runid保存下来(我们查询不到),当出现网络抖动时,Slave会将此runid发送给Master,Master根据此runid判断进行全量同步还是增量同步。

  • runid与现在Master的runid一致:说明网络是同一个Master节点,增量同步条件满足(具体是否执行还需要看复制积压缓冲区是否有溢出)。
  • runid与现在Master的runid不一致:说明是新的Master节点,进行全量同步。
1.2.3.3 复制积压缓冲区

复制积压缓冲区(replication backlog buffer):复制积压缓冲区是Master节点创建的一个先进先出的队列,默认大小为1MB,用于备份主节点的传播命令,所有的Slave共享一个复制积压缓冲区。

Slave将offset发送给Master之后,Master会根据当前的offset值来决定是全量同步还是增量同步。

  • 如果offset偏移量之后的数据仍在缓冲区中(意味着在执行bgsave时,缓冲区还未满,未溢出),则执行增量同步(runid要是同一个)。
  • 如果offset偏移量之后的数据不在缓冲区中(意味着在执行bgsave时间过长,缓冲区的数据已经被挤出,数据溢出),此时执行全量同步

完整同步过程如下:

image

总结:在网络抖动情况下,增量同步的条件:

1)offset之后的数据仍在复制积压缓冲区中(在Master执行bgsave期间,复制积压缓冲区未发现溢出)

2)runid要是同一个

1.2.3.4 心跳机制

在命令传播阶段,Master与Slave之间通过心跳机制来保证Master与Slave双方正常连接在线。

  • Master定时向Slave发送ping,查看对方是否在线,通过repl-ping-Slave-period参数维护,默认10s
127.0.0.1:6379> config get repl-ping-Slave-period
1) "repl-ping-Slave-period"
2) "10"
127.0.0.1:6379>
  • Slave定时向Master发送 replcconf ack<偏移量>命令,频率为每秒发送一次。

作用1):用于检测Slave与Master的连接状态,可以在Master端使用info命令查看replication组的lag值,代表上一次接收到此Slave的replcconf ack命令间隔。

image

作用2):Slave发送当前的offset来到Master与之对比,如果出现网络丢包情况下那么Slave与Master之间的offset不一致,通常是Master发了多个字节,而Slave由于网络原因没有接到那么多,可通过offset判断出上一条指令是否丢失;如果丢失,主服务器可从复制积压缓冲区找出丢失的指令,重新命令传播。

2.8版本以前无法检测命令是否丢失,因此存在主从数据不一致的风险。

作用3):Slave每次发送replcconf ack命令给Master,Master用于确认有多少个Slave与之连接,并且还可以确定多个Slave与Master上一次发送心跳的时间(lag),如果Slave长时间未发送心跳来到Master,可以先将Master暂停写的操作。

  • min-Slaves-to-write:当前Master的Slave数量小于此值则暂停写操作
min-Slaves-to-write 3
  • min-Slaves-max-lag:当前Master的Slave的lag都等于此值则暂停写的操作
min-Slaves-max-lag 5
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
上一篇:源码都没调试过,怎么能说熟悉 redis 呢? 下一篇:很抱歉没有了
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐