在介绍redis哨兵之前,先来建立一下模拟哨兵需要搭建的环境(此方法用于快速生成多个容器并统一管理,如不怕麻烦可跳过)。通过之前docker的文章,我们知道可以通过镜像生成容器,可以快速的部署环境,但是却只能单一的生成,每次都需要通过docker run命令来运行,这对于部署多个redis是很不方便的,那么我们可以通过docker-compose
来解决这个问题。
docker-compose 安装
运行命令(提前设置好对应的版本):
- curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.1/docker-compose-
uname -s
-uname -m
> /usr/local/bin/docker-compose - chmod +x /usr/local/bin/docker-compose
这样docker compose就安装完成了。我们在安装docker镜像用到的Dockerfile可以填写我们需要的配置,docker compose 同样也有对应的配置文件,docker-composer.yml或docker-composer.yaml文件。
下面我们来简单介绍一下该文件的几个比较重要的配置项(下面是主节点的配置部分):
version : “3.6” : docker引擎对应所支持的docker-compose文本格式
services:本工程的服务配置列表
master-1:自定义服务名
image:指定的镜像名称
container_name:对应容器名称
environment:环境变量,其中对应的REALIP,PORT可以用来后续统一配置
networks:加入指定网络(redis-network这是自己创建的网络名称【bridge】)
working_dir:工作目录
ports:指定对应的端口(我们通过docker构建的环境是没办法直接与客户端连接的,需要通过外部Linux服务器的端口通过映射连接)
volumes:挂载目录
entrypoint:可以去运行脚本文件
这个是已搭建好的容易(master,slave主从节点,以及3个sentinel哨兵节点):
哨兵的作用
我们知道redis的主从,可以实现读写分离,负载均衡等来完成我们的业务需求,但是也会出现对应的问题,一旦我们的主节点宕机,要么人口处理重新选择主节点,建立关联,要么只能停止服务。哨兵的作用就是替代了这种需要人工干预的处理方式。
哨兵机制的原理
Redis Sentinel 一个分布式架构,其中包含若干个 Sentinel 节点和 Redis 数据节点,每个Sentinel 节点会对数据节点和其余 Sentinel 节点进行监控,当它发现节点不可达时,会对节点做下线标识。
如果被标识的是主节点,它还会和其他 Sentinel 节点进行“协商”,当大多数 Sentinel 节点都认为主节点不可达时,它们会选举出一个 Sentinel 节点来完成自动故障转移的工作,同时会将这个变化实时通知给 Redis 应用方。整个过程完全是自动的,不需要人工来介入。
故障转移流程
- 主节点出现故障,此时两个从节点与主节点失去连接,主从复制失败。
- 每个 Sentinel 节点通过定期监控发现主节点出现了故障
- 多个 Sentinel 节点对主节点的故障达成一致会选举出其中一个节点作为领导者负责故障转移。
- Sentinel 领导者节点执行了故障转移,整个过程基本是跟我们手动调整一致的,只不过是自动化完成的。
- 故障转移后整个 Redis Sentinel 的结构,重新选举了新的主节点
Redis Sentinel功能
- 监控: Sentinel 节点会定期检测 Redis 数据节点、其余 Sentinel 节点是否可达
- 通知:Sentinel 节点会将故障转移的结果通知给应用方
- 主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系
- 配置提供者:在 Redis Sentinel 结构中,客户端在初始化的时候连接的是 Sentinel 节点集合 ,从中获取主节点信息。
需要注意的是 Sentinel 节点本身就是独立的 Redis 节点,只不过它们有一些特殊,它们不存储数据, 只支持部分命令
Redis Sentinel核心配置
此处对应的配置文件为:sentinel.conf
- sentinel monitor mymaster 127.0.0.1 7000 2
监控的主节点的名字、IP 和端口,最后一个2的意思是有几台 Sentinel 发现有问题,就会发生故障转移,例如 配置为2,代表至少有2个 Sentinel 节点认为主节点不可达,那么这个不可达的判定才是客观的。对于设置的越小,那么达到下线的条件越宽松,反之越严格。一般建议将其设置为 Sentinel 节点的一半加1,我们通常设置sentinel节点数至少3个,且为奇数个(便于后续的投票处理)
注意:最后的参数不得大于conut(sentinel)
- sentinel down-after-millseconds mymaster 30000
这个是超时的时间(单位为毫秒)。就是说如果超过30秒无法连接,那么则认为该节点存在问题
- sentinel parallel-syncs mymaster 1
当 Sentinel 节点集合对主节点故障判定达成一致时, Sentinel 领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作, parallel-syncs 就是用来限制在一次故障转移之后,每次向新的主节点发起复制操作的从节点个数,指出 Sentinel 属于并发还是串行。通常设置为1,可以减轻master的压力
- sentinel auth-pass
如果 Sentinel 监控的主节点配置了密码,sentinel auth-pass 配置通过添加主节点的密码,防止 Sentinel 节点对主节点无法监控
- sentinel failover-timeout mymaster 180000
表示故障转移的时间。
上述内容配置好后,需要在sentinel节点去进行关联:redis-sentinel 【上述的配置文件路径】
以构建好的:主从 + 哨兵
接下来我们开始模拟master节点意外关闭的情况,停止运行master容器,等待我们在哨兵节点设置的无法连接超过的时长,我们可以通过日志查看哨兵节点重新选择后的从节点变为了主节点
sentinel 日志说明
- +slave-reconf-done :从服务器已经成功完成对新主服务器的同步。
- -dup-sentinel :对给定主服务器进行监视的一个或多个 Sentinel 已经因为重复出现而被移除 —— 当 Sentinel 实例重启的时
候,就会出现这种情况。 - +sentinel :一个监视给定主服务器的新 Sentinel 已经被识别并添加。
- +sdown :给定的实例现在处于主观下线状态。
- -sdown :给定的实例已经不再处于主观下线状态。
- +odown :给定的实例现在处于客观下线状态。
- -odown :给定的实例已经不再处于客观下线状态。
- +new-epoch :当前的纪元(epoch)已经被更新。
- +try-failover :一个新的故障迁移操作正在执行中,等待被大多数 Sentinel 选中(waiting to be elected by themajority)。
- +elected-leader :赢得指定纪元的选举,可以进行故障迁移操作了。
- +failover-state-select-slave :故障转移操作现在处于 select-slave 状态 —— Sentinel 正在寻找可以升级为主服务器的从
服务器。 - no-good-slave :Sentinel 操作未能找到适合进行升级的从服务器。Sentinel 会在一段时间之后再次尝试寻找合适的从服务器
来进行升级,又或者直接放弃执行故障转移操作。 - selected-slave :Sentinel 顺利找到适合进行升级的从服务器。
- failover-state-send-slaveof-noone :Sentinel 正在将指定的从服务器升级为主服务器,等待升级功能完成。
- failover-end-for-timeout :故障转移因为超时而中止,不过最终所有从服务器都会开始复制新的主服务器(slaves will
eventually be configured to replicate with the new master anyway)。 - failover-end :故障转移操作顺利完成。所有从服务器都开始复制新的主服务器了。
- +switch-master :配置变更,主服务器的 IP 和地址已经改变。 这是绝大多数外部用户都关
心的信息。
现在我们可以得知我们布置的哨兵机制可以在主节点意外关闭的情况下,帮助我们选择一个从节点作为新的主节点,使其能够继续进行工作(代替人工操作)
选择合适的slave节点:在我们的redis节点配置文件中有slave-priority的选项,它可以用来设置权重,有点类似于负载均衡的设置,数值越大,越易被选择。当此配置没有被设置时,哨兵节点会去查询各个节点的偏移量,选择偏移量最大的点。若偏移量也大体相同,那么选择runid最小的slave节点。
哨兵机制实现的原理
我们的哨兵之所以能够监测redis运行情况并完成迁移,是因为通过3个定时任务对节点进行监测,给所认定出现问题的节点标注状态,然后通过投票的方式,选举出领导者,进行故障转移。
- 每10秒每个 Sentinel 对 Master 和 Slave 执行一次 Info Replication 。
- 每2秒每个 Sentinel 通过 Master 节点的 channel 交换信息(pub/sub)。
- 每1秒每个 Sentinel 对其他 Sentinel 和 Redis 执行 ping 。
第一个定时任务,指的是 Redis Sentinel 可以对 Redis 节点做失败判断和故障转移,在 Redis 内部有三个定时任务作为基础,来 Info Replication 发现Slave 节点,这个命令可以确定主从关系。
第两个定时任务,类似于发布订阅, Sentinel 会对主从关系进行判定,通过 sentinel:hello 频道交互。了解主从关系可以帮助更好的自动化操作 Redis。然后 Sentinel 会告知系统消息给其它 Sentinel 节点,最终达到共识,同时 Sentinel 节点能够互相感知到对方。
第三个定时任务,指的是对每个节点和其它 Sentinel 进行心跳检测,它是失败判定的依据
主观下线
这是我们之前的配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
主观下线状态
每个 Sentinel 节点对 Redis 节点失败的“偏见”。之所以是偏见,只是因为某一台机器30秒内没有得到回复。
客观下线状态
这个时候需要所有 Sentinel 节点都发现它30秒内无回复,才会达到共识。
领导者选举
- 每个做主观下线的sentinel节点,会向其他的sentinel节点发送命令,要求将它设置成为领导者
- 收到命令sentinel节点,如果没有同意通过其它节点发送的命令,那么就会同意请求,否则就会拒绝
- 如果sentinel节点发现自己票数超过半数,同时也超过了 sentinel monitor mymaster 127.0.0.1 6379 2 超过2个的时候,就会成为领导者
- 进行故障转移操作
哨兵常见问题
- 异步复制导致数据丢失:因为master->slave的复制是异步,所以可能有部分还没来得及复制到slave就宕机了,此时这些部分数据就丢失了。
- 集群脑裂导致数据丢失:脑裂,也就是说,某个master所在机器突然脱离了正常的网络,跟其它slave机器不能连接,但是实际上master还运行着。此时哨兵可能就会认为master宕机了,然后开始选举,将其它 slave 切换成 master 。这时候集群里就会有2个 master 。此时虽然某个 slave 被切换成了 master ,但是可能client还没来得及切换成新的 master ,还继续写向旧的 master 的数据可能就丢失了。因此旧master再次恢复的时候,会被作为一个 slave 挂到新的 master 上去,自己的数据会被清空,重新从新的 master 复制数据。
我们可以通过以下两个配置来尽可能的解决这两个问题:
- min-slaves-to-write 1
在异步复制的过程当中,通过 min-slaves-to-write 这个配置确定最小的可写从库个数,就可以确保的说,一旦 slave 复制数据和 ack 延迟时间太长,就认为可能 master 宕机后损失的数据太多了,那么就拒绝写请求,这样就可以把master宕机时由于部分数据未同步到 slave 导致的数据丢失降低到可控范围内
- min-slaves-max-lag 10
集群脑裂因为 client 还没来得及切换成新的 master ,还继续写向旧的 master 的数据可能就丢失了通过 min-slaves-to-write 确保必须是有多少个从节点连接,并且延迟时间小于 min-slaves-max-lag 多少秒。