原因:

最近在复习Redis的时候,学习到了为了提高Redis集群的高可用性,有一个模式为哨兵模式哨兵模式的作用是为了在主节点出现阻塞或者错误,无法接收数据的时候,及时将从节点切换为主节点,由此保证Redis集群能够保持正常状态,保持高可用。

但是尽管引入哨兵模式能够提高集群的高可用性,但是随之带来的有数据丢失数据不一致问题。这些问题的原因有可能是因为主从异步复制的时候,主节点挂了,导致子节点接收数据不完整,出现数据不一致问题。也有可能是因为出现了脑裂问题,导致数据丢失问题等等。

但是知道概念后,就需要实践一下才会知道这些问题出现的原因,避免纸上谈兵。

部署节点:

那么一共需要的是六个节点,也就是要启动六个Redis服务来模拟集群,那这里我使用Docker-Compose来实现集群。

1. 创建文件夹

一共是六个节点,分别是一个主机,两个从机,三个哨兵。所以我们需要创建六个文件夹来对应这六个节点。

这是最终创建的结构树。



.

|-- docker-compose.yml

|-- master

| |-- conf

| | `-- redis.conf

| `-- data

| |-- dump.rdb

| `-- nodes.conf

|-- sentinel1

| |-- conf

| | `-- sentinel.conf

| `-- data

|-- sentinel2

| |-- conf

| | `-- sentinel.conf

| `-- data

|-- sentinel3

| |-- conf

| | `-- sentinel.conf

| `-- data

|-- slave1

| |-- conf

| | `-- redis.conf

| `-- data

| `-- dump.rdb

`-- slave2

|-- conf

| `-- redis.conf

`-- data

`-- dump.rdb

从Redis官网获取最新的Redis.conf,并复制到master,slave1,slave2的conf文件夹中。并获取sentinel.conf复制到sentinel1,sentinel2,sentinel3的conf文件夹中。

2.编写配置:

然后修改一下配置。

  • 主机


bind 0.0.0.0 #设置所有地址访问

protected-mode yes #这个是默认开启的,也就是开启安全模式

requirepass 123456 #设置密码

以上的配置无论是主机还是从机都要配置,这是一样的。

  • 从机


replica-read-only yes #这个配置是从机只能读,不能写

replicaof 172.20.1.2 6379 #配置主机的ip和端口 在redis5.0以前则是salveof配置

masterauth 123456 #因为主节点设置了密码,必须设置这个,否则会连不上主节点

这里说一下,在Vi下编辑文档,查找,另起一行的命令如下。



/你要查找的词 #按N往上找 按n往下找

o #直接在当前行下另起一行

  • 哨兵


#这个配置的作用就是设置监听的master节点的信息,mymaster可以换成符合规定的其他名字,后面的2是指当有两个sentinel认为#这个master失效了,才会认为失效,从而进行主从切换

sentinel monitor mymaster 172.20.1.2 6379 2

#配置主从的的密码,注意mymaster要对应刚才的配置项

sentinel auth-pass mymaster 123456

#这个配置项指定了需要多少失效时间,一个master才会被这个sentinel主观地认为是不可用的。 单位是毫秒,默认为30秒

sentinel down-after-milliseconds mymaster 30000

#这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。值越大,slave复制的越快,但同时也对主节点的网络和硬盘负载造成压力

sentinel parallel-syncs mymaster 1

#定义故障切换超时时间。默认180000,单位秒,即3min。

sentinel failover-timeout mymaster 180000

#设置运行期是不能改变notification-script和 client-reconfig-script ,避免一些安全问题

sentinel deny-scripts-reconfig yes

3.编写docker-compose文件

然后就是编写docker-compose文件了。



version: '3'

services:

master:

image: redis:latest

container_name: redis_master #master节点

volumes:

- ./master/conf/redis.conf:/etc/redis/redis.conf

- ./master/data:/data

networks:

redis_network:

ipv4_address: 172.20.1.2

command: /bin/bash -c "redis-server /etc/redis/redis.conf" #这句话就是要加载这个路径下的配置

environment:

- TZ=Asia/Shanghai

- LANG=en_US.UTF-8

ports:

- "6379:6379"

slave1:

image: redis:latest

container_name: redis_slave_1 #slave1节点

volumes:

- ./slave1/conf/redis.conf:/etc/redis/redis.conf

- ./slave1/data:/data

networks:

redis_network:

ipv4_address: 172.20.1.3

command: /bin/bash -c "redis-server /etc/redis/redis.conf"

environment:

- TZ=Asia/Shanghai

- LANG=en_US.UTF-8

ports:

- "6380:6379"

slave2:

image: redis:latest

container_name: redis_slave_2 #slave2节点

volumes:

- ./slave2/conf/redis.conf:/etc/redis/redis.conf

- ./slave2/data:/data

networks:

redis_network:

ipv4_address: 172.20.1.4

command: /bin/bash -c "redis-server /etc/redis/redis.conf"

environment:

- TZ=Asia/Shanghai

- LANG=en_US.UTF-8

ports:

- "6381:6379"

sentinel1:

image: redis:latest

container_name: redis_sentinel_1 #sentinel1节点

ports:

- "26379:26379"

volumes:

- ./sentinel1/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf

networks:

redis_network:

ipv4_address: 172.20.1.5

command: /bin/bash -c "redis-sentinel /usr/local/etc/redis/sentinel.conf"

sentinel2:

image: redis:latest

container_name: redis_sentinel_2 #sentinel2节点

ports:

- "26380:26379"

volumes:

- ./sentinel2/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf

networks:

redis_network:

ipv4_address: 172.20.1.6

command: /bin/bash -c "redis-sentinel /usr/local/etc/redis/sentinel.conf"

sentinel3:

image: redis:latest

container_name: redis_sentinel_3 #sentinel3节点

ports:

- "26381:26379"

volumes:

- ./sentinel3/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf

networks:

redis_network:

ipv4_address: 172.20.1.7

command: /bin/bash -c "redis-sentinel /usr/local/etc/redis/sentinel.conf"

networks:

redis_network:

driver: bridge

ipam:

config:

- subnet: 172.20.1.0/24

执行docker-compose up -d创建容器。

redis两台哨兵模式 redis哨兵多主_bash

一共出现六个容器,如果某个容器不见了,那就证明配置有误。执行docker logs 容器id来查看日志。

新建三个终端来分别进入master,slave和sentinel节点。

4.验证状态

使用命令



docker exec -it redis_master bash

#进入后使用命令进入redis-cli,-a是指密码,-h是指ip,-p是指端口

redis-cli -a 123456 -h 172.20.1.2 -p 6379

#使用命令查看从机信息

info replication

redis两台哨兵模式 redis哨兵多主_redis两台哨兵模式_02

从节点有两个,ip也给出了。

然后我们按照刚才的命令进入从机,试一下创建一个key,发现出现错误。这就是刚才从机配置的replica-read-only yes配置在发挥作用了。因为按照我们的设定,一主两从,主节点是负责写,从节点负责读,读写分离,那么从节点当然无法写入数据。

redis两台哨兵模式 redis哨兵多主_架构_03

我们进入sentinel节点,注意:进入redis-cli的端口不是6379了,而是刚才配置的26379端口。执行info命令,往下划。

redis两台哨兵模式 redis哨兵多主_架构_04

可以看到sentinel节点监控的master节点只有一个,而且ip也正是我们的master主机ip,slaves为2,sentinels为3,这说明我们的配置的一主两从三哨兵是正常运行的。

测试:

我们尝试在主节点写入数据,看看是否会同步到从机中。

redis两台哨兵模式 redis哨兵多主_redis两台哨兵模式_05

我们在从机查看是否有该key。

redis两台哨兵模式 redis哨兵多主_redis两台哨兵模式_06

从机的确能获取到主机所设置的key值,说明主从同步是正常的。

同时读写分离是Redis自带的,通过配置slave,Redis会自动地让从机进行读操作,让主机进行写操作。这是Redis的主从模式所自带的。

而在主从模式的基础上添加哨兵模式,从而提高主从模式的高可用。

模拟故障

这里先模拟一个最常见的故障,就是master主机宕机,看看是否会进行主从切换。

这里直接stop掉master主机的容器。

然后过个30秒,在Sentinel的容器上执行命令



info Sentinel

redis两台哨兵模式 redis哨兵多主_redis两台哨兵模式_07

然后发现master主机已经改变了,slave2从机节点被切换成为新的master节点。

总结:

通过以上步骤,我们完成了一主两从三哨兵的搭建,同时也通过模拟一个最最常见的故障了测试哨兵模式的主从切换功能。那就来总结一下哨兵模式的功能吧。

  1. 故障转移,能够通过配置及时地将从机切换成主机。
  2. 故障发现,能够通过Ping监控Master状态。
  3. 配置中心,能够统一配置所有节点的主节点信息。

优点:

  • 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
  • 主从可以自动切换,系统更健壮,可用性更高。
  • Sentinel 会不断的检查 主服务器 和 从服务器 是否正常运行。当被监控的某个 Redis 服务器出现问题,Sentinel 通过API脚本向管理员或者其他的应用程序发送通知。

缺点:

  • Redis较难支持在线扩容,对于集群,容量达到上限时在线扩容会变得很复杂。

主从模式解决了Redis的xx,哨兵模式解决了Redis的高可用性问题,但是面对在线扩容则显得困难,所以才有了Cluster集群模式,通过水平拓展Redis节点,从而解决了扩容这个问题。后面我们会继续研究Cluster集群的搭建与它的一些讨论。

同时主从模式和哨兵模式中有一些功能值得我们去深究,例如Sentinel是如何通知其他从机切换主机的呢?Sentinel的投票仲裁机制是怎么样的?