Redis - 主从复制那些事

Replication,也就是我们所说的主从复制,主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主。可以联想一下MySQL的主从复制(读写分离),理念都是相通的。

主要业务场景 :读写分离和容灾恢复。

Redis的Replication往往是配从(库)不配主(库)。

【1】Replication准备–conf文件复制与修改

这里为三台服务器演示做准备,将conf文件复制多份,模拟启动多个redis服务。

① 拷贝多个redis.conf文件

[root@localhost redis]# cp 6379.conf 6380.conf
[root@localhost redis]# cp 6379.conf 6381.conf
[root@localhost redis]# ll
总用量 180
-rw-r--r-- 1 root root 57828 6月  29 16:12 6379.conf
-rw-r--r-- 1 root root 57828 8月  19 16:06 6380.conf
-rw-r--r-- 1 root root 57828 8月  19 16:06 6381.conf

② 修改conf文件

这里以6379.conf为例,其他两个类同。

  • 开启daemonize yes
daemonize yes
  • 修改端口
port 6379
  • pid文件名字
pidfile /var/run/redis_6379.pid
  • log文件名字
logfile "/home/data/redis-log/redis6379.log"
  • dump.rdb名字
dbfilename dump6379.rdb

【2】Replication常见模式 - 一主二仆

① 以不同配置文件启动三个redis服务

# 在6379窗口下
[root@localhost bin] redis-server /etc/redis/6379.conf

# 在6380窗口下
[root@localhost bin] redis-server /etc/redis/6380.conf

# 在6381窗口下
[root@localhost bin] redis-server /etc/redis/6381.conf

这里写图片描述


查看启动的redis服务示意如下:

这里写图片描述


查看redis启动日志:

这里写图片描述

日志文件路径下分别生成redis6379.conf redis6380.conf和redis6381.conf。


② info replication命令

分别在三个窗口中连接三个redis client 并执行命令info replication

6379中执行命令示意如下:

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_replid:ddd1085ab0d6b1ade8261b82ad966b2ee208bac5
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379>

如图所示,此时为三个master:

这里写图片描述


③ slave命令转换角色

这里假设79 为主机,80 和81为从机。

第一步在6379中分别放入三个键值:

这里写图片描述

第二步分别在6380和6381中执行如下命令:

slaveof 127.0.0.1 6379

#查看此时角色
info replication

需要注意的是,命令方式配置完,从机每次与master断开之后,都需要重新连接,除非配置进redis.conf文件

如下图所示,此时形成了一主二仆:

这里写图片描述

第三步,6379主机放入新的键值,然后测试6380和6381是否能够获取新旧键值

这里写图片描述

如上图所示,从机可以获取到主机的新旧键值(新旧以执行slave命令为分割)。

查看主机6379日志:

这里写图片描述

查看从机6380日志:

这里写图片描述


④ 如果三个服务执行一样的命令呢?

比如,三个服务都要执行如下命令:

set k5 v5

主机6379正常执行,从机尝试set操作时则会报异常:

这里写图片描述


⑤ 如果主机shutdown呢?从机是上位还是原地待命?

如下图所示:

这里写图片描述

从机可以正常读取信息,但是角色未发生变化(仍旧为从机),主机连接状态已经从up转变为了down。


⑥ 主机又回来了后,主机新增记录,从机还能否顺利复制?

如下所示,将6379启动,查看从机6380状态:

这里写图片描述

6380中master_link_status:up不再是down。

主机6379放入新的键值,从机尝试获取:

这里写图片描述

如上图所示,从机成功获取主机新的键值!


⑦ 如果从机down掉,然后恢复还能跟上大部队吗?

从机down掉,主机6379放入新的键值,启动从机然后查看其状态信息并尝试获取6379的新的键值。

这里写图片描述

如上图所示,此时从机角色为master,再次执行命令slave后才能获取主机新放入的键值(包括主机旧的键值)。


【3】一主二仆演变之薪火相传

接【2】继续演示,如果主机down了,从机虽然可以读,但是业务没法再往redis里面写数据了。这是很可怕的一件事情!

而且从另外一个角度考虑,一主X仆有个明显的缺陷就是中心化太严重,master承担压力和风险较高。

薪火相传是这种思想,上一个Slave可以是下一个slave的Master,Slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master,可以有效减轻master的写压力。

如果中途变更转向则会清除之前的数据,重新建立拷贝最新的。

#6380不变,6381切换宿主
127.0.0.16381>slaveof 127.0.0.1 6380

如下图所示,6379为6380的主机,6380为6381的主机:

这里写图片描述

需要注意的是,6380此时显示的角色为slave。


主机6379放入新的键值,6380和6381尝试获取:

这里写图片描述

这种方式可以减轻主机的压力,但是也有个明显的缺陷,暂且不说主机6379宕掉,如果从机6380宕掉呢?接在6380上的从机将不能够获取主机6379新的键值!!


【4】一主二仆演变之反客为主

接上面,将6381重新切回到6379上面:

127.0.0.1:6381>slaveof 127.0.0.1 6379

这里写图片描述


如【2】中演示,主机挂掉,从机原地待命,这就有个可能性,为什么从机不能反客为主作为主机呢?

命令如下:

slaveof no one 
# 使当前数据库停止与其他数据库的同步,转成主数据库

演示如下:将6379 shutdown,6380执行如上命令:

这里写图片描述

如上图所示,此时6380已经成为master,可以正常存取键值。


6381的主机6379已经down掉,那么需要切换主机到6380:

这里写图片描述

如上图所示,从6380中再次重新同步数据,正常获取6380中的键值!

此时6380与6381形成了一主一从的格局,即使6379此时恢复正常,已经无力回天。

反客为主可以解决主机down掉的问题,看起来很友好,但是需要手动切换为mater,能不能自动?


【5】哨兵模式大宝剑

哨兵模式:反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

① 恢复环境,6379下挂着6380和6381

这里写图片描述


② 在/etc/redis下面建立sentinel.conf文件,名字绝不能错

目录可以自定义,这里将配置文件统一放在了etc/redis下面。

命令如下:

touch sentinel.conf

这里写图片描述

一组sentinel能同时监控多个Master。


③ 配置哨兵,填写内容

sentinel monitor 被监控数据库名字(自己起名字) 127.0.0.1 6379 1

上面最后一个数字1,表示主机挂掉后salve投票看让谁接替成为主机,得票数多少后成为主机。

这里写图片描述

解释:如果主机6379挂掉后,剩下的从机谁的票数多于一票,谁就成为新的master。


④ 启动哨兵

如下图所示,切换到/usr/local/bin目录下,执行命令:

redis-sentinel /etc/reids/sentinel.conf 

#上述目录依照各自的实际情况配置,可能目录不同

这里写图片描述


⑤ 正常的一主二仆中6379挂掉,查看新master选举

这里写图片描述

此时查看6380和6381状态:

这里写图片描述

如上图所示6381成为了新的master,6380自动切换了宿主到6381!


⑥ 如果之前的master重启回来,会不会双master冲突?

其实从⑤中哨兵的日志中就应该可以猜测到答案,即使6379回来了,回成为6381的从机。

如下图所示,哨兵检测到6379复活后,会将其变为6381(新master)的从机:
这里写图片描述
这里写图片描述


【6】几点理论

实践和理论永远是缺一不可的。

① Redis复制功能实现

Redis 的复制功能分为同步(sync)和命令传播(command propagate)两个阶段,具体来说同步指的是主将自己的现有状态复制给从的过程;而命令传播指的是之后一旦主发生新的状态修改之后,需要及时告知从的过程。

  • 同步

当需要从去复制主的时候,从会首先执行同步操作(因为这时候一般主从间的数据差异较大)

  • 从会向主发送 SYNC 命令
  • 主收到之后执行 BGSAVE 命令,在后台生成一个 RDB 文件,并开始将这个时间点时候的写操作存储在缓冲区中
  • 主将 RDB 文件发送给从,从载入这个文件,将自己的状态更新完成,此时从跟主* 之间的差异就在于主新增的操作了
  • 主将自己新增的操作也发生给从,从逐步更新完成,愈发接近主的实际状态

  • 命令传播

这之后,从跟主之间已经完成同步,但是如果主接受写操作,那么瞬间二者又会不一致,主需要通过命令传播的方式将新的写命令传递给从,此时的更新量已经较少,不需要依靠 RDB 文件进行大量的同步,而只需要依靠命令为单位就可以。


②增量/全量机制

如果主从链接断了怎么办,或者说从机down掉又恢复,究竟是从重新全量同步主的状态,直接舍弃之前已经同步过的数据;还是采用新的方法记录断线前已经同步完的位置,只要重连之后同步新的操作就可以?

实际上前者是旧版的复制功能,后者就是新版(3.X之后)的改进。

3.X后的Redis版本中 PSYNC 命令替代了单纯的 SYNC ,其中 PSYNC 具有全量重同步和部分重同步两种模式,其中完整重同步用于处理初次复制的情况,类似于之前旧版的 SYNC ;而部分重同步则是用于处理断线重连的情况,只需要同步新的更改,采用 CONTINUE 标记是否进行部分重同步,提高了效率。

具体实现

这里主要讨论部分重同步的具体实现方案了。主要有以下三部分构成:

  • 主和从的复制偏移量,表示二者分别进行到哪里了,主侧每发送 N 个字节的数据,自身维护的 offset + N ; 从侧每次收到 N 个字节的数据,自身的 offset + N 。由此在重连之后,从主要上报一下自己的 offset ,主就可以知道从和主之间差了多少数据。
  • 主的缓冲区,表示断线期间的新增操作,是由主维护的一个 FIFO 的队列,默认大小为 1 MB 。 当进行命令传播操作时,主会将传播的命令写进缓冲区,这里如果主收到了从断线重连之后上报的 offset ,会先看看缺少的数据是否还存在缓冲区中,若存在则恢复从进行部分重同步;反之进行全量重同步。
  • 服务器的运行 ID 用于标识服务器身份。启动时自动生成,由 40 个随机的十六进制字符组成,在进行初次复制时从会保存主传过来的自身的运行 ID ,表示从是具体同步哪个主,一旦发生断线重连,那么需要检测重连上的主是否是之前的主,如果是才决定接下来是部分重同步还是全量重同步;如果重连上的已经不是之间的主了,那么必须全量重同步。

猜你喜欢

转载自blog.csdn.net/J080624/article/details/81839902