文章目录
- 前言
- 主从复制的问题
- 怎么人工恢复故障主节点
- Redis Setinel 架构
- 使用 docker 来配置哨兵结构
- 安装 docker
- 编排 redis 主从节点
- 编排 redis 哨兵节点
- 观察哨兵模式的作用
- 主从切换的具体流程
- 小结
前言
redis 主从复制模式下, 一旦主节点出现故障, 不能提供服务的时候, 就需要人工进行主从切换. 这是十分不靠谱的, 毕竟谁也不知道主节点什么时候出现故障, 这样就无法第一时间恢复服务. 所以 redis 从 2.8 版本开始提供了 哨兵(Redis Sentinel) 模式, 可以通过自动化手段来解决主节点出现故障的问题.
主从复制的问题
redis 主从复制是将主节点的数据同步给从节点, 这样从节点就拥有主节点的所有数据, 这样客户端就可以从从节点这边来读取数据, 来分担主节点所要承受的并发量. 但是主从复制也有一些问题:
- 主节点发生故障的时候, 这时整个 redis 服务就失去了写入数据的功能, 这时就需要程序员手动干预来将从节点切换成主节点.
- 主从复制可以将读的并发量分担给从节点, 但是写压力/存储压力是无法被分担的, 还是受到单机的限制.
其中第一个问题就是哨兵模式主要解决的问题, 而第二个问题是 redis 集群模式解决的问题, 本章主要讨论第一个问题.
怎么人工恢复故障主节点
首先程序员会先看看主节点挂的原因, 并且尝试能不能抢救, 如果知道原因并且容易恢复主节点的功能, 就直接将主节点恢复即可.
如果不好定位原因, 或者知道原因, 但是短时间之内不能恢复, 这时, 程序员就需要挑一个从节点, 设置成主节点:
- 在挑选出来的从节点上执行 slaveof no one, 使其脱离故障的主节点.
- 在其它的从节点上执行 slaveof 新主节点ip 新主节点端口, 使其连上新的主节点.
- 告知客户端(修改客户端的配置), 让客户端能够连接新的主节点, 用来完成修改数据的操作.
当之前挂了的主节点修好了之后, 就可以作为一个新的从节点, 加入到这个分布式系统中.
上述恢复操作涉及到了人工干预, 人工干预的过程也是有可能出错的, 如果配置的时候不小心配置错误了, 可能就会导致更严重的问题. 而且人工干预也是需要时间的, 在集群配置好之前, 整个 redis 服务都不能进行写操作, 这段时间多少都会造成损失.
所以, redis 引入了哨兵模式, 就能解决上述问题.
Redis Setinel 架构
如图所示, redis 哨兵模式也是加入了一个集合, 里面有若干个哨兵节点, 这几个哨兵节点会监控现有的 redis master 和 slave(建立 tcp 长连接, 并定期发送心跳包). 借助监控机制, 就可以及时发现某个主机是否挂了. 如果从节点挂了, 不会有多大影响, 并不影响整个集群的读写功能. 如果是主节点挂了, 哨兵就要发挥作用了.
- 此时一个哨兵节点发现主节点挂了, 还不够, 需要有多个哨兵节点来认同这个主节点挂了(防止单个哨兵误判).
- 如果主节点确实挂了, 这些哨兵节点中就会挑选出一个 leader, 由这个 leader 负责从现有的从节点中, 挑选一个作为新的主节点.
- 挑选出新的主节点之后, 哨兵节点就会自动控制该被选中的从节点, 执行 slaveof no one, 并且控制其他从节点, 修改主从结构到新的主节点上.
- 哨兵节点会自动通知客户端程序新的主节点是哪个, 并且后续客户端再进行写操作, 就会针对新的主节点进行操作了.
redis 哨兵核心功能:
- 监控: 监控主节点的状态, 当主节点出现故障, 及时做出处理
- 自动的故障转移: 自动的完成挑选从节点替换主节点操作
- 通知: 通知客户端新的主节点
如果只设置一个哨兵节点, 会出现的问题:
- 单个哨兵节点如果故障了, 就无法监控主节点的情况, 就无法进行自动恢复的过程了.
- 出现误判的概率高, 网络传输的数据容易出现抖动, 延迟或者丢包的, 如果只有一个哨兵节点, 出现上述问题的概率就很高, 就会导致单个哨兵节点出现误判, 影响比较大.
使用 docker 来配置哨兵结构
安装 docker
这里使用一台服务器来部署哨兵结构, 由于手动部署多个节点非常麻烦, 所以使用 docker 来配置更为轻松.
此处是基于 Ubuntu 环境进行配置
- 安装 docker 和 docker-compose
apt install docker-compose
- 停止之前的 redis-server
service redis-server stop
- 使用 docker 来获取 redis 镜像
docker pull redis:5.0.9
编排 redis 主从节点
- 创建 docker 的配置文件 docker-compose.yml
mkdir redis
cd redis
mkdir redis-data
cd redis-data
vim 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_default
- 启动所有容器
docker-compose up -d
编排 redis 哨兵节点
- 创建 docker 的配置文件 docker-compose.yml
cd redis
mkdir redis-sentinel
vim 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_default
- 创建哨兵节点的配置文件
创建三个配置文件 sentinel1.conf, sentinel2.conf, sentinel3.conf, 其中三个配置文件的内容都相同:
bind 0.0.0.0
port 26379
sentinel monitor redis-master redis-master 6379 2
sentinel down-after-milliseconds redis-master 1000
解释一下配置文件中的配置:
- sentinel monitor 主节点名 主节点ip 主节点端口 法定票数
- 其中, 法定票数的解释: 比如当工作时, 其中某个哨兵节点的网络出现问题, 但是主节点仍正常工作, 此时这个哨兵节点就可能产生误判, 认为主节点挂了. 所以哨兵模式是采用投票的方式来判断主节点是否挂了, 这个方案更为稳妥. 当某个哨兵节点发现主节点挂了之后, 就会发起投票, 当认为主节点挂了的票数 >= 法定票数的时候, 哨兵集群才真正认为主节点挂了, 从而采取后面进一步的操作.
- sentinel down-after-milliseconds 主节点ip 超时时间
- 主节点和哨兵之间根据心跳包来进行沟通, 如果心跳包在指定时间之内还没有响应, 就认为主节点挂了.
- 启动所有容器
docker-compose up -d
到这步, redis 哨兵结构就配置完成了.
观察哨兵模式的作用
此时已经将三个主从节点和三个哨兵节点都提起来了:
这时, 手动将主节点停掉, 来模拟主节点故障:
docker stop redis-master
当主节点挂了之后, 哨兵节点就开始工作了, 观察哨兵节点的日志:
- 在 redis-sentinel 目录下
docker-compose logs
由于三个节点的日志都大差不差, 只观察一个节点的日志即可:
可以看到, 当 master 节点挂了之后, 哨兵节点首先先发现主节点 sdown, 然后开始投票, 当票数达到 3/2 的时候, 达到法定票数, 于是 master 节点被判定为 odown.
- sdown: 主观下线(SubjectivelyDown), 当前哨兵节点认为该主节点挂了.
- odown: 客观下线(ObjectivelyDown), 多个哨兵节点都认为该节挂了, 达成了一直(达到法定票数), 此时才认为 master 节点确实挂了.
接下来, 哨兵节点就会挑选出一个节点来作为主节点, 如图中的 switch-master 操作
主从切换的具体流程
-
主观下线: 哨兵节点通过心跳包判定 redis 服务器是否正常工作, 如果心跳包在一定时间内没有响应, 说明 redis 服务器挂了. 此时还不能排除网络波动的影响, 因此只能单方面认为 redis 主节点挂了.
-
客观下线: 多个哨兵节点认为主节点挂了, 即认为主节点挂了的哨兵节点个数达到法定票数, 此时就认为是客观下线.
-
让多个哨兵节点选出一个 leader 节点, 由这个 leader 负责选出一个从节点作为新的主节点
-
观察上述 sentinel1 的日志可以看到:通过刚刚的配置文件中的信息可以得知:
sentinel1 的 id 为: bc48b2a2e35a8a0fedb95cd5095916f5c9b560db
sentinel2 的 id 为: 7ca7cb5c9d0707e9558aa984fb647ebfb8d0223f
sentinel3 的 id 为: ba6a0e8374a1efbc5837ce8301a9d0b498468ea7
上述过程中, sentinel1 先给自己投了一票, 接着 sentinel2 给自己投了一票, sentinel3 给 sentinel2 投了一票, 此时 sentinel2 就为 leader.
-
-
此时 leader 选组完毕, leader 就需要挑选一个从节点来作为新的主节点.
- 挑选规则:
- 比较优先级: 在配置文件中的 slave-priority 或者 replica-priority 可以设置优先级, 优先级高的就会胜出.
- 比较 offset: 比较从节点从主节点中同步数据的进度, offset 越大, 说明从主节点这边同步的数据越多, offset 大的节点会被挑选成为主节点
- run id: redis 节点启动时生成的随机数, 谁的 id 小, 谁就会被挑选.
- 挑选规则:
当新的主节点指定好之后, leader 就会控制这个节点, 执行 slaveof no one, 成为 master, 再控制其它节点, 执行 slaveof, 让这些节点以新的 master 作为主节点.
小结
上述过程都是自动完成的, 这样就解决了主节点宕机之后需要人工干预的问题, 提高了系统的稳定性和可用性.
还需要注意一些事项:
- 哨兵节点不能只有一个, 否则哨兵节点挂了也会影响系统的可用性.
- 哨兵节点最好是奇数个, 方便选举 leader, 得票容易超过半数.
- 哨兵节点不负责存储数据, redis 主节点负责存储.
- 主从复制 + 哨兵解决的问题是 “提高可用性”, 不能解决 “数据极端情况下写丢失” 的问题.
- 主从复制 + 哨兵不能提高数据的存储容量. 当我们需要存的数据接近或者超过机器的物理内存, 这样的结构就难以胜任了, 这时就需要下一章节所讲的 redis 集群来解决了.