【Redis】Centos 7 搭建 redis 主从复制以及哨兵模式

【Redis】Centos 7 搭建 redis 主从复制以及哨兵模式

Scroll Down

安装单机版的redis,可以看之前的博客。

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

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

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

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

①拷贝多个redis.conf文件

之前的博客已经在/usr/local/redis/bin下把conf拷贝进去了

这里因为是做主从复制,所以需要三个,因为我们再cp两个。

[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

port 6379

pidfile /var/run/redis_6379.pid

logfile "/hme/data/redis-log/redis6379.log"

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

image.png

② info replication命令,可以看到79是master

image.png

③ slave命令转换角色

这里假设79 为主机,80 和81为从机。
第一步在6379中分别放入三个键值:
image.png

第二步分别在6380和6381中执行如下命令(设置从机跟随主机):

//将6380 6381作为从机依赖6379主机
slaveof 127.0.0.1 6379

#查看此时角色
info replication

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

image.png

image.png

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

image.png

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

image.png
image.png
image.png

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

查看主机6379日志:

image.png!

查看从机6380日志:

image.png

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

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

set k5 v5

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

image.png

很明显,从机是没权限设置key val的

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

image.png!

杀掉了6379的,只有6380 6381
image.png

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

6380 6381 能正常获取到数据的,并且6380 6381的角色并没有发生改变,且还是不能设置key val的

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

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

image.png

6380中master_link_status:up不再是down。
主机6379放入新的键值,从机尝试获取:

image.png

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


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

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

image.png

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

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

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

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

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

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

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

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

image.png!

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

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

image.png

image.png!

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

就像接龙, 6380接6379的数据,6381接6380的数据


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

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

127.0.0.1:6381>slaveof 127.0.0.1 6379

image.png!


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

命令如下:

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

演示如下:将6379 shutdown,6380执行如上命令:
image.png!

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


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

image.png

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

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

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


【5】哨兵模式大宝剑

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

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

6380 6381 重新slaveof 127.0.0.1 6379


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

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

命令如下:

touch sentinel.conf

image.png

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


③ 配置哨兵,填写内容

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

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

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

④ 启动哨兵

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

redis-sentinel /etc/redis/sentinel.conf 
#上述目录依照各自的实际情况配置,可能目录不同

image.png


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

image.png

此时查看6380和6381状态:

image.png

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

如下所示,是再次测试6381挂掉之后,6379被选举为master的哨兵日志:

23456:X 11 Jan 18:21:10.708 # Sentinel ID is 12854a3954a5d4e8697c7ba46a799b6ac0123f28
23456:X 11 Jan 18:21:10.709 # +monitor master host6379 127.0.0.1 6381 quorum 1
23456:X 11 Jan 18:23:17.993 # +sdown master host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:17.993 # +odown master host6379 127.0.0.1 6381 #quorum 1/1
23456:X 11 Jan 18:23:17.993 # +new-epoch 2
23456:X 11 Jan 18:23:17.994 # +try-failover master host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:18.040 # +vote-for-leader 12854a3954a5d4e8697c7ba46a799b6ac0123f28 2
23456:X 11 Jan 18:23:18.040 # +elected-leader master host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:18.041 # +failover-state-select-slave master host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:18.112 # +selected-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:18.112 * +failover-state-send-slaveof-noone slave 127.0.0.1:6379 127.0.0.1 6379 @ host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:18.184 * +failover-state-wait-promotion slave 127.0.0.1:6379 127.0.0.1 6379 @ host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:18.229 # +promoted-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:18.229 # +failover-state-reconf-slaves master host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:18.267 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:19.221 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:19.221 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:19.308 # +failover-end master host6379 127.0.0.1 6381
23456:X 11 Jan 18:23:19.308 # +switch-master host6379 127.0.0.1 6381 127.0.0.1 6379
23456:X 11 Jan 18:23:19.308 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6379
23456:X 11 Jan 18:23:19.308 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6379
23456:X 11 Jan 18:23:49.324 # +sdown slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6379
23456:X 11 Jan 18:25:26.992 # -sdown slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6379
23456:X 11 Jan 18:25:36.950 * +convert-to-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6379

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

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

如下图所示,哨兵检测到6379复活后,会将其变为6381(新master)的从机:
image.png

image.png


【6】几点理论

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

① Redis复制功能实现

Redis 的复制功能分为同步(sync)和命令传播(command propagate)两个阶段。具体来说同步指的是主将自己的现有状态复制给从的过程,将从服务器的数据库状态更新成主服务器当前的数据库状态。而命令传播指的是当主服务器数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的过程。

在 Redis 2.8 版本之前,进行主从复制时一定会顺序执行上述两个步骤,而从 2.8 开始则可能只需要执行命令传播即可。

  • 同步
    当需要从去复制主的时候,从会首先执行同步操作(因为这时候一般主从间的数据差异较大)步骤如下:
  • 从会向主发送 SYNC 命令
  • 主收到SYNC 之后执行 BGSAVE 命令,在后台生成一个 RDB 文件,并开始将这个时间点时候的写操作存储在缓冲区中
  • bgsave 执行完成后主将 RDB 文件发送给从,从载入这个文件,将自己的状态更新完成,此时从跟主 之间的差异就在于主新增的操作了
  • 主将自己新增的操作也发生给从,从逐步更新完成,愈发接近主的实际状态

image.png

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

如果再次执行同步操作而非命令传播是否可以将从的状态与主保持一致呢?

的确可以,但是没必要。因为实现同步的 sync 命令是一个非常消耗资源的操作,具体说明如下(每次执行SYNC命令,主从服务器需要执行以下动作):

  • 主服务器需要执行bgsave命令来生成rdb文件,这个生成操作会耗费主服务器大量的CPU、内存和磁盘IO资源。
  • 主服务器需要将自己生成的rdb文件发送给从服务器,这个发送操作会耗费主从服务器大量的网络资源(带宽和流量),并对主服务器响应命令请求的时间产生影响。
  • 接收到rdb文件的从服务器需要载入主服务器发来的rdb文件,并且在载入期间,从服务器会因为阻塞而没办法处理命令请求。

②增量/全量机制-PSYNC优化

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

主从同步实际分 2 种情况:

  • 初次复制:从服务器第一次复制当前主服务器(PS:主服务器是有可能更换的)
  • 断线后重复制:处于命令传播阶段的主从服务器,因为网络问题而中断复制,从服务器通过自动重连,重新连接上主服务器并继续复制。

在断线后重复制的情况下,在 2.8 版本之前,会再次执行同步(sync 命令)和命令传播。如果说,在断线期间,主服务器(已有上万键值对)只执行了几个写命令,为了让从服务器弥补这几个命令,却要重新执行 sync 来生成新的 rdb 文件,这也是非常低效的。

为了解决这个问题,2.8 开始就使用 psync 命令来代替 sync 命令去执行同步操作。其中 PSYNC 具有全量重同步和部分重同步两种模式。其中完整重同步用于处理初次复制的情况,类似于之前旧版的 SYNC ;而部分重同步则是用于处理断线重连的情况,只需要同步新的更改,采用 CONTINUE 标记是否进行部分重同步,提高了效率。

部分重同步具体实现

主要有以下三部分构成:

  • 主和从的复制偏移量,表示二者分别进行到哪里了。
    主侧每发送 N 个字节的数据,自身维护的 offset + N ; 从侧每次收到 N 个字节的数据,自身的 offset + N 。由此在重连之后,从主要上报一下自己的 offset ,主就可以知道从和主之间差了多少数据。
  • 主服务器的复制积压缓冲区
    表示断线期间的新增操作,是由主维护的一个 FIFO 的队列,默认大小为 1 MB 。 当进行命令传播操作时,主还会将传播的命令写进缓冲区,这里如果主收到了从断线重连之后上报的 offset ,会先看看缺少的数据是否还存在缓冲区中,若存在则恢复从进行部分重同步;反之进行全量重同步。

image.png

  • 服务器的运行 ID(run id)
    用于标识服务器身份,启动时自动生成,由 40 个随机的十六进制字符组成。在进行初次复制时从会保存主传过来的运行 ID ,表示从是具体同步哪个主。一旦发生断线重连,那么需要检测重连上的主是否是之前的主。如果是才决定接下来是部分重同步还是全量重同步;如果重连上的已经不是之间的主了,那么必须全量重同步。

③ 心跳检测

刚才提到,主从同步有同步和命令传播 2 个步骤。

当完成了同步之后,主从服务器就会进入命令传播阶段,此时从服务器会以每秒 1 次的频率,向主服务器发送命令:REPLCONF ACK <replication_offset> ,其中 replication_offset 是从服务器当前的复制偏移量

发送这个命令主要有三个作用:

  • 检测主从服务器的网络状态
  • 辅助实现 min-slaves 选项
  • 检测命令丢失(若丢失,主服务器会将丢失的写命令重新发给从服务器)

【7】两个问题

① 单个Sentinel进程是否可行?

答案:当然不可行,而且上面sentinel配置也是很简陋

② 一主二从三哨兵配置下,当进行主从切换时,前台应用怎么办?

image.png

比如master-6379挂了,哨兵选举了slave-6380为新的master。那么此时SpringBoot怎么办?SpringBoot中配置的redis host还是6379。