思维导图基本概念哨兵 (Sentinel)主从复制模式下一旦主节点由于故障不能提供服务需要人工进行主从切换同时大量的客户端需要被通知切换到新的主节点上对于上了一定规模的应用来说这种方案是无法接受的于是 Redis 从 2.8 开始提供了 Redis Sentinel哨兵加个来解决这个问题。• Redis Sentinel 的概念• Redis Sentinel 的部署• Redis Sentinel 命令• Redis Sentinel 客户端• Redis Sentinel 实现原理由于对 Redis 的许多概念都有不同的名词解释所以在介绍 Redis Sentinel 之前先对几个名词概念进行必要的说明如表所示。Redis Sentinel 相关名词解释Redis Sentinel 是 Redis 的高可用实现方案在实际的生产环境中对提高整个系统的高可用是非常有帮助的主从复制的问题Redis 的主从复制模式可以将主节点的数据改变同步给从节点这样从节点就可以起到两个作用第一作为主节点的一个备份一旦主节点出了故障不可达的情况从节点可以作为后备 “顶” 上来并且保证数据尽量不丢失主从复制表现为最终一致性。第二从节点可以分担主节点上的读压力让主节点只承担写请求的处理将所有的读请求负载均衡到各个从节点上。但是主从复制模式并不是万能的它同样遗留下以下几个问题1. 主节点发生故障时进行主备切换的过程是复杂的需要完全的人工参与导致故障恢复时间无法保障。2.主节点可以将读压力分散出去但写压力/存储压力是无法被分担的还是受到单机的限制。其中第一个问题是高可用问题即 Redis 哨兵主要解决的问题。第二个问题是属于存储分布式的问题留给 Redis 集群去解决人工恢复主节点故障Redis 主从复制模式下主节点故障后需要进行的人工工作是比较繁琐的1运维人员通过监控系统发现 Redis 主节点故障宕机。2运维人员从所有节点中选择一个此处选择了 slave 1执行 slaveof no one使其作为新的主节点。3运维人员让剩余从节点此处为 slave 2执行 slaveof {newMasterIp} {newMasterPort} 从新主节点开始数据同步。4更新应用方连接的主节点信息到 {newMasterIp} {newMasterPort}。5如果原来的主节点恢复执行 slaveof {newMasterIp} {newMasterPort} 让其成为一个从节点。上述过程可以看到基本需要人工介入无法被认为架构是高可用的。而这就是 Redis Sentinel 所要做的。哨兵自动恢复主节点故障当主节点出现故障时Redis Sentinel 能自动完成故障发现和故障转移并通知应用方从而实现真正的高可用。Redis Sentinel 是一个分布式架构(Redis 数据节点、Sentinel 节点集合、客户端分布在多个物理节点上)。其中包含若干个 Sentinel 节点和 Redis 数据节点每个Sentinel 节点会对数据节点和其余 Sentinel 节点进行监控当它发现节点不可达时会对节点做下线表示。如果下线的是主节点它还会和其他的 Sentinel 节点进行 “协商”当大多数 Sentinel 节点对主节点不可达这个结论达成共识之后它们会在内部 “选举” 出一个领导节点来完成自动故障转移的工作同时将这个变化实时通知给 Redis 应用方。整个过程是完全自动的不需要人工介入。整体的架构如图所示。Redis Sentinel 架构Redis Sentinel 相比于主从复制模式是多了若干建议保持奇数Sentinel 节点用于实现监控数据节点哨兵节点会定期监控所有节点包含数据节点和其他哨兵节点。针对主节点故障的情况故障转移流程大致如下1主节点故障从节点同步连接中断主从复制停止。2哨兵节点通过定期监控发现主节点出现故障。哨兵节点与其他哨兵节点进行协商达成多数认同主节点故障的共识。这步主要是防止该情况出故障的不是主节点而是发现故障的哨兵节点该情况经常发生于哨兵节点的网络被孤立的场景下。监控这些进程之间会建立tcp长链接定期发送心跳包3哨兵节点之间使用 Raft 算法选举出一个领导角色由该节点负责后续的故障转移工作。4哨兵领导者开始执行故障转移从节点中选择一个作为新主节点让其他从节点同步新主节点通知应用层转移到新主节点。Redis Sentinel 具有以下几个功能• 监控: Sentinel 节点会定期检测 Redis 数据节点、其余哨兵节点是否可达。• 故障转移: 实现从节点晋升promotion为主节点并维护后续正确的主从关系。• 通知: Sentinel 节点会将故障转移的结果通知给应用方。安装部署 (基于 docker)实际工作中多个节点放在一个服务器上没有意义。按理说这6个节点一个部署在六个不同的服务器主机上但我们只有一个云服务器我们就只有在一个服务器上来搭建环境了。由于节点数量多且依赖的端口号配置文件等比较多直接部署的话比较麻烦就像我们之前部署的637963806381一样。所以我们使用docker来解决上述问题。docker我还没学完后面会展示学习笔记的docker在现在的企业级开发中也会经常用到建议系统的学习。准备工作1) 安装 docker 和 docker-composedocker-compose 的安装# ubuntuapt install docker-compose# centosyum install docker-compose2) 停止之前的 redis-server# 停止 redis-serverservice redis-server stop# 停止 redis-sentinel 如果已经有的话.service redis-sentinel stop3) 使用 docker 获取 redis 镜像docker pull redis:5.0.9编排 redis 主从节点1) 编写 docker-compose.yml创建redis/docker-compose.yml , 同时 cd 到 yml 所在目录中.将下面的内容写入yml文件里注意yml的缩进格式。version: 3.7 services: master: image: redis:5.0.9 container_name: redis-master restart: always command: redis-server --appendonly yes ports: - 6379:6379 slave1: image: redis:5.0.9 container_name: redis-slave1 restart: always command: redis-server --appendonly yes --slaveof redis-master 6379 ports: - 6380:6379 slave2: image: redis:5.0.9 container_name: redis-slave2 restart: always command: redis-server --appendonly yes --slaveof redis-master 6379 ports: - 6381:63792) 启动所有容器docker compose up -d如果启动后发现前面的配置有误, 需要重新操作, 使用 docker-compose down 即可停止并删除刚才创建好的容器.3) 查看运行日志docker compose logs上述操作必须保证工作目录在 yml 的同级目录中, 才能工作4) 验证连接主节点 redis-cli -p 6379。从节点63806381都没文体我这里就放一张主节点的图编排 redis-sentinel 节点也可以把 redis-sentinel 放到和上面的 redis 的同一个 yml 中进行容器编排. 此处分成两组, 主要是为了两方面:• 观察日志方便• 确保 redis 主从节点启动之后才启动 redis-sentinel. 如果先启动 redis-sentinel 的话, 可能触发额外的选举过程, 混淆视听. (不是说先启动哨兵不行, 而是观察的结果可能存在一定随机性).1) 编写 docker-compose.yml创建 redis-sentinel/docker-compose.yml , 同时 cd 到 yml 所在目录中.注意: 每个目录中只能存在一个 docker-compose.yml 文件.version: 3.7 services: sentinel1: image: redis:5.0.9 container_name: redis-sentinel-1 restart: always command: redis-sentinel /etc/redis/sentinel.conf volumes: - ./sentinel1.conf:/etc/redis/sentinel.conf ports: - 26379:26379 sentinel2: image: redis:5.0.9 container_name: redis-sentinel-2 restart: always command: redis-sentinel /etc/redis/sentinel.conf volumes: - ./sentinel2.conf:/etc/redis/sentinel.conf ports: - 26380:26379 sentinel3: image: redis:5.0.9 container_name: redis-sentinel-3 restart: always command: redis-sentinel /etc/redis/sentinel.conf volumes: - ./sentinel3.conf:/etc/redis/sentinel.conf ports: - 26381:26379 networks: default: external: name: redis-data_default2) 创建配置文件创建 sentinel1.conf sentinel2.conf sentinel3.conf . 三份文件的内容是完全相同的.都放到redis-sentinel/ 目录中.bind 0.0.0.0port 26379sentinel monitor redis-master redis-master 6379 2sentinel down-after-milliseconds redis-master 1000理解 sentinel monitorsentinel monitor 主节点名 主节点ip 主节点端口 法定票数• 主节点名, 这个是哨兵内部自己起的名字.• 主节点 ip, 部署 redis-master 的设备 ip. 此处由于是使用 docker, 可以直接写 docker 的容器名, 会被自动 DNS 成对应的容器 ip• 主节点端口, 不解释.• 法定票数, 哨兵需要判定主节点是否挂了. 但是有的时候可能因为特殊情况, 比如主节点仍然工作正常, 但是哨兵节点自己网络出问题了, 无法访问到主节点了. 此时就可能会使该哨兵节点认为主节点下线, 出现误判. 使用投票的方式来确定主节点是否真的挂了是更稳妥的做法. 需要多个哨兵都认为主节点挂了, 票数 法定票数 之后, 才会真的认为主节点是挂了.理解sentinel down-after-milliseconds• 主节点和哨兵之间通过心跳包来进行沟通. 如果心跳包在指定的时间内还没回来, 就视为是节点出现故障.既然内容相同, 为啥要创建多份配置文件?redis-sentinel 在运行中可能会对配置进行 rewrite, 修改文件内容. 如果用一份文件, 就可能出现修改混乱的情况.3) 启动所有容器docker compose up -d4) 查看运行日志docker compose logs5) 观察 redis-sentinel 的配置 rewrite再次打开哨兵的配置文件, 发现文件内容已经被自动修改了.# Generated by CONFIG REWRITE 这里的内容就是自动修改的.可以自己手动对比这三份文件, 可以看到配置内容是存在差异的.重新选举redis-master 宕机之后我们先手动把 redis-master 干掉docker stop redis-master观察哨兵的日志docker compose logs可以看到哨兵发现了主节点 sdown, 进一步的由于主节点宕机得票达到 3/2 , 达到法定得票, 于是master 被判定为 odown.• 主观下线 (Subjectively Down, SDown): 哨兵感知到主节点没心跳了. 判定为主观下线.• 客观下线 (Objectively Down, ODown): 多个哨兵达成一致意见, 才能认为 master 确实下线了.此时可以看到主节点6379无法连接redis-master 重启之后手动把 redis-master 启动起来然后查看日志docker start redis-master可以看到刚才新启动的 redis-master 被当成了 slave我们再info replication分别看一下6381和6370可以看到6381role为主节点原来的主节点6379变为了从节点结论• Redis 主节点如果宕机, 哨兵会把其中的一个从节点, 提拔成主节点.• 当之前的 Redis 主节点重启之后, 这个主节点被加入到哨兵的监控中, 但是只会被作为从节点使用.选举原理当前环境如上方介绍, 三个哨兵(sentenal1, sentenal2, sentenal3), 一个主节点(redis-master), 两个从节点(redis-slave1, redis-slave2).当主节点出现故障, 就会触发一系列过程.1) 主观下线当 redis-master 宕机, 此时 redis-master 和三个哨兵之间的心跳包就没有了.此时, 站在三个哨兵的角度来看, redis-master 出现严重故障. 因此三个哨兵均会把 redis-master 判定为主观下线(SDown)2) 客观下线此时, 哨兵 sentenal1, sentenal2, sentenal3 均会对主节点故障这件事情进行投票. 当故障得票数 配置的法定票数之后,sentinel monitor redis-master 172.22.0.4 6379 2在这个地方配置的 2 , 即为法定票数此时意味着 redis-master 故障这个事情被做实了. 此时触发客观下线 (ODown)3) 选举出哨兵的 leader接下来需要哨兵把剩余的 slave 中挑选出一个新的 master. 这个工作不需要所有的哨兵都参与. 只需要选出个代表 (称为 leader), 由 leader 负责进行 slave 升级到 master 的提拔过程.这个选举的过程涉及到 Raft 算法假定一共三个哨兵节点, S1, S2, S31. 每个哨兵节点都给其他所有哨兵节点, 发起一个 拉票请求. (S1 - S2, S1 - S3, S2 - S1, S2 - S3,S3 - S1, S3 - S2)2. 收到拉票请求的节点, 会回复一个 投票响应. 响应的结果有两种可能, 投 or 不投.比如 S1 给 S2 发了个投票请求, S2 就会给 S1 返回投票响应. 到底 S2 是否要投 S1 呢? 取决于 S2 是否给别人投过票了. (每个哨兵只有一票).如果 S2 没有给别人投过票, 换而言之, S1 是第一个向 S2 拉票的, 那么 S2 就会投 S1. 否则则不投.3. 一轮投票完成之后, 发现得票超过半数的节点, 自动成为 leader. 如果出现平票的情况 (S1 投 S2, S2 投 S3, S3 投 S1, 每人一票), 就重新再投一次即可.这也是为啥建议哨兵节点设置成奇数个的原因. 如果是偶数个, 则增大了平票的概率, 带来不必要的开销.4. leader 节点负责挑选一个 slave 成为新的 master. 当其他的 sentenal 发现新的 master 出现了, 就说明选举结束了.Raft 算法的核心就是 先下手为强. 谁率先发出了拉票请求, 谁就有更大的概率成为 leader.这里的决定因素成了 网络延时. 网络延时本身就带有一定随机性.具体选出的哪个节点是 leader, 这个不重要, 重要的是能选出一个节点即可.4) leader 挑选出合适的 slave 成为新的 master挑选规则:1. 比较优先级. 优先级高(数值小的)的上位. 优先级是配置文件中的配置项( slave-priority 或replica-priority ).2. 比较 replication offset 谁复制的数据多, 高的上位.3. 比较 run id , 谁的 id 小, 谁上位.当某个 slave 节点被指定为 master 之后,1. leader 指定该节点执行 slave no one , 成为 master2. leader 指定剩余的 slave 节点, 都依附于这个新 master小结上述过程, 都是 无人值守 , Redis 自动完成的. 这样做就解决了主节点宕机之后需要人工干预的问题,提高了系统的稳定性和可用性.一些注意事项:• 哨兵节点不能只有一个. 否则哨兵节点挂了也会影响系统可用性.•哨兵节点最好是奇数个. 方便选举 leader,得票更容易超过半数.•哨兵节点不负责存储数据. 仍然是 redis 主从节点负责存储.• 哨兵 主从复制解决的问题是 提高可用性, 不能解决 数据极端情况下写丢失的问题.• 哨兵 主从复制不能提高数据的存储容量. 当我们需要存的数据接近或者超过机器的物理内存, 这样的结构就难以胜任了.为了能存储更多的数据, 就引入了集群.