Redis高级-哨兵机制
Redis高级-哨兵机制
哨兵模式
Redis Sentinel,即Redis哨兵,在Redis 2.8版本开始引入。哨兵的核心功能是主节点的自动故障转移。
哨兵简介
哨兵概念
首先我们来看一个业务场景:如果redis的master宕机了,此时应该怎么办?
那此时我们可能需要从一堆的slave中重新选举出一个新的master,那这个操作过程是什么样的呢?这里面会有什么问题出现呢?

要实现这些功能,我们就需要redis的哨兵,那哨兵是什么呢?
哨兵
哨兵(sentinel) 是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master。
哨兵作用
哨兵的作用:
监控:监控master和slave
不断的检查master和slave是否正常运行
master存活检测、master与slave运行情况检测
通知(提醒):当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知
配置提供者:客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。
自动故障转移:断开master与slave连接,选取一个slave作为master,将其他slave连接新的master,并告知客户端新的服务器地址
注意:哨兵也是一台redis服务器,只是不提供数据相关服务,通常哨兵的数量配置为单数
哨兵集群的组建
上图中哨兵集群是如何组件的呢?哨兵实例之间可以相互发现,要归功于 Redis 提供的 pub/sub 机制,也就是发布 / 订阅机制。
在主从集群中,主库上有一个名为__sentinel__:hello
的频道,不同哨兵就是通过它来相互发现,实现互相通信的。在下图中,哨兵 1 把自己的 IP(172.16.19.3)和端口(26579)发布到__sentinel__:hello
频道上,哨兵 2 和 3 订阅了该频道。那么此时,哨兵 2 和 3 就可以从这个频道直接获取哨兵 1 的 IP 地址和端口号。然后,哨兵 2、3 可以和哨兵 1 建立网络连接

通过这个方式,哨兵 2 和 3 也可以建立网络连接,这样一来,哨兵集群就形成了。它们相互间可以通过网络连接进行通信,比如说对主库有没有下线这件事儿进行判断和协商。
哨兵工作原理
哨兵在进行主从切换过程中经历三个阶段
- 监控
- 通知
- 故障转移
监控
用于同步各个节点的状态信息
获取各个sentinel的状态(是否在线)
获取master的状态
获取所有slave的状态(根据master中的slave信息)
其内部的工作原理具体如下:
这是由哨兵向主库发送 INFO 命令来完成的。就像下图所示,哨兵 2 给主库发送 INFO 命令,主库接受到这个命令后,就会把从库列表返回给哨兵。接着,哨兵就可以根据从库列表中的连接信息,和每个从库建立连接,并在这个连接上持续地对从库进行监控。哨兵 1 和 3 可以通过相同的方法和从库建立连接

通知
sentinel在通知阶段要不断的去获取master/slave的信息,然后在各个sentinel之间进行共享,具体的流程如下:

故障转移
当master宕机后sentinel是如何知晓并判断出master是真的宕机了呢?
首先要理解两个概念:主观下线和客观下线
- 主观下线:任何一个哨兵都是可以监控探测,并作出Redis节点下线的判断;
- 客观下线:有哨兵集群共同决定Redis节点是否下线;
我们来看具体的操作流程
当某个哨兵(如下图中的哨兵2)判断主库“主观下线”后,就会给其他哨兵发送 is-master-down-by-addr
命令。接着,其他哨兵会根据自己和主库的连接情况,做出 Y 或 N 的响应,Y 相当于赞成票,N 相当于反对票
如果赞成票数(这里是2)是大于等于哨兵配置文件中的 quorum
配置项(比如这里如果是quorum=2), 则可以判定主库客观下线了。

哨兵集群的选举
哨兵的选举机制其实很简单,就是一个Raft选举算法: 选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举
- 任何一个想成为 Leader 的哨兵,要满足两个条件
- 第一,拿到半数以上的赞成票;
- 第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。
当sentinel认定master下线之后,此时需要决定更换master,那这件事由哪个sentinel来做呢?这时候sentinel之间要进行选举,如下图所示:

在选举的时候每一个人手里都有一票,而每一个人的又都想当这个处理事故的人,那怎么办?
大家就开始抢,于是每个人都会发出一个指令,在内网里边告诉大家我要当选举人,比如说现在的sentinel1和sentinel4发出这个选举指令了,那么sentinel2既能接到sentinel1的也能接到sentinel4的,接到了他们的申请以后呢,sentinel2他就会把他的一票投给其中一方,投给谁呢?谁先过来我投给谁,假设sentinel1先过来,所以这个票就给到了sentinel1。那么给过去以后呢,现在sentinel1就拿到了一票,按照这样的一种形式,最终会有一个选举结果。对应的选举最终得票多的,那自然就成为了处理事故的人。需要注意在这个过程中有可能会存在失败的现象,就是一轮选举完没有选取,那就会接着进行第二轮第三轮直到完成选举。
选举机制
接下来就是由选举胜出的sentinel去从slave中选一个新的master出来的工作,这个流程是什么样的呢?
首先它有一个在服务器列表中挑选备选master的原则
不在线的OUT
响应慢的OUT
与原master断开时间久的OUT
优先原则
优先级 offset(复制偏移量最大,只复制最完整的从节点) runid
选出新的master之后,发送指令( sentinel )给其他的slave:
向新的master发送slaveof no one
向其他slave发送slaveof 新masterIP端口
总结:故障转移阶段
- 发现问题,主观下线与客观下线
- 竞选负责人
- 优选新master
- 新master上任,其他slave切换master,原master作为slave故障恢复后连接

集群Cluster(分片)
主从复制和哨兵机制保障了高可用,就读写分离而言虽然slave节点扩展了主从的读并发能力,但是写能力和存储能力是无法进行扩展,就只能是master节点能够承载的上限。如果面对海量数据那么必然需要构建master(主节点分片)之间的集群,同时必然需要吸收高可用(主从复制和哨兵机制)能力,即每个master分片节点还需要有slave节点,这是分布式系统中典型的纵向扩展(集群的分片技术)的体现;所以在Redis 3.0版本中对应的设计就是Redis Cluster
现状问题:业务发展过程中遇到的峰值瓶颈
- redis提供的服务OPS可以达到10万/秒,当前业务OPS已经达到10万/秒
- 内存单机容量达到256G,当前业务需求内存容量1T
- 使用集群的方式可以快速解决上述问题
集群简介
集群就是使用网络将若干台计算机联通起来,并提供统一的管理方式,使其对外呈现单机的服务效果
集群作用:
- 分散单台服务器的访问压力,实现负载均衡
- 分散单台服务器的存储压力,实现可扩展性
- 降低单台服务器宕机带来的业务灾难
哈希槽(Hash Slot)
Redis-cluster没有使用一致性hash,而是引入了哈希槽的概念。Redis-cluster中有16384(即2的14次方)个哈希槽,每个key通过CRC16校验后对16383取模来决定放置哪个槽。Cluster中的每个节点负责一部分hash槽(hash slot)。
比如集群中存在三个节点,则可能存在的一种分配如下:
- 节点A包含0到5500号哈希槽;
- 节点B包含5501到11000号哈希槽;
- 节点C包含11001 到 16384号哈希槽。
Cluster集群结构设计
数据存储设计:
通过算法设计,计算出key应该保存的位置
将所有的存储空间计划切割成16384份,每台主机保存一部分
注意:每份代表的是一个存储空间,不是一个key的保存空间
将key按照计算出的结果放到对应的存储空间

那redis的集群是如何增强可扩展性的呢?譬如我们要增加一个集群节点

当我们查找数据时,集群是如何操作的呢?
- 各个数据库相互通信,保存各个库中槽的编号数据
- 一次命中,直接返回
- 一次未命中,告知具体位置

Cluster集群结构搭建
首先要明确的几个要点:
- 配置服务器(3主3从)
- 建立通信(Meet)
- 分槽(Slot)
- 搭建主从(master-slave)
Cluster配置
- 是否启用cluster,加入cluster节点
cluster-enabled yes|no
- cluster配置文件名,该文件属于自动生成,仅用于快速查找文件并查询文件内容
cluster-config-file filename
- 节点服务响应超时时间,用于判定该节点是否下线或切换为从节点
cluster-node-timeout milliseconds
- master连接的slave最小数量
cluster-migration-barrier min_slave_number
Cluster节点操作命令
- 查看集群节点信息
cluster nodes
- 更改slave指向新的master
cluster replicate master-id
- 发现一个新节点,新增master
cluster meet ip:port
- 忽略一个没有solt的节点
cluster forget server_id
- 手动故障转移
cluster failover
集群操作命令:
- 创建集群
redis-cli –-cluster create masterhost1:masterport1 masterhost2:masterport2 masterhost3:masterport3 [masterhostn:masterportn …] slavehost1:slaveport1 slavehost2:slaveport2 slavehost3:slaveport3 -–cluster-replicas n
注意:master与slave的数量要匹配,一个master对应n个slave,由最后的参数n决定
master与slave的匹配顺序为第一个master与前n个slave分为一组,形成主从结构
- 添加master到当前集群中,连接时可以指定任意现有节点地址与端口
redis-cli --cluster add-node new-master-host:new-master-port now-host:now-port
- 添加slave
redis-cli --cluster add-node new-slave-host:new-slave-port master-host:master-port --cluster-slave --cluster-master-id masterid
- 删除节点,如果删除的节点是master,必须保障其中没有槽slot
redis-cli --cluster del-node del-slave-host:del-slave-port del-slave-id
- 重新分槽,分槽是从具有槽的master中划分一部分给其他master,过程中不创建新的槽
redis-cli --cluster reshard new-master-host:new-master:port --cluster-from src- master-id1, src-master-id2, src-master-idn --cluster-to target-master-id -- cluster-slots slots
注意:将需要参与分槽的所有masterid不分先后顺序添加到参数中,使用,分隔
指定目标得到的槽的数量,所有的槽将平均从每个来源的master处获取
- 重新分配槽,从具有槽的master中分配指定数量的槽到另一个master中,常用于清空指定master中的槽
redis-cli --cluster reshard src-master-host:src-master-port --cluster-from src- master-id --cluster-to target-master-id --cluster-slots slots --cluster-yes