前言
在开始本章的讲解之前,我们首先从宏观角度回顾一下 Redis 实现高可用相关的技术。它们包括:持久化、复制、哨兵和集群,在本系列的前篇文章介绍了持久化以及复制的原理以及实现。本文将对剩下的两种高可用技术哨兵、集群进行讲解,讲一讲它们是如何进一步提高系统的高可用性?
Redis 的主从复制模式下,一旦主节点由于故障不能提供服务,需要手动将从节点晋升为主节点,同时还要通知客户端更新主节点地址,这种故障处理方式从一定程度上是无法接受的。Redis 2.8 以后提供了 Redis Sentinel 哨兵机制来解决这个问题。
在 Redis 3.0 之前,使用哨兵(sentinel)机制来监控各个节点之间的状态。Redis Cluster 是 Redis 的分布式解决方案,在 3.0 版本正式推出,有效地解决了 Redis 在分布式方面的需求。当遇到单机内存、并发、流量等瓶颈时,可以采用 Cluster 架构方案达到负载均衡的目的。
Redis HA 实践(Redis Sentinel)
Redis Sentinel 概述
Sentinel(哨岗、哨兵)是 Redis 的高可用(high availability)解决方案:由一个或多个 Sentinel 实例(instance)组成的 Sentinel 系统(system)可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。
当 server1 的下线时长超过用户设定的下线时长上限时,Sentinel 系统就会对 server1 执行故障转移操作:
- 首先,Sentinel 系统会挑选 server1 属下的其中一个从服务器,并将这个被选中的从服务升级为新的主服务器。
- 之后,Sentinel 系统会向 server1 属下的所有从服务器发送新的复制指令,让他们成为新的主服务器的从服务器,当所有从服务器都开始复制新的主服务器时,故障转移操作执行完毕。
- 另外,Sentinel 还会继续监视已下线的 server1,并在它重新上线时,将它设置为新的主服务器的从服务器。
Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务:
- 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
- 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
Redis Sentinel 重点总结
- Sentinel 只是一个运行在特殊模式下的 Redis 服务器,因此初始化服务器时将普通 Redis 服务器使用的代码替换成 Sentinel 专门代码,它使用了和普通模式不同的命令表,所以 Sentinel 模式能够使用的命令和普通 Redis 服务器能够使用的命令不同。
- Sentinel 会读入用户指定的配置文件,为每个要被监视的主服务器创建相应的实例结构,并创建连向主服务器的命令连接和订阅连接,其中命令连接用于向主服务器发送命令请求,而订阅连接则用于接收指定频道的消息。
- Sentinel 通过主服务器发送 INFO 命令来获得主服务器属下所有从服务器的地址信息,并为这些从服务器创建相应的实例结构,以及连向这些从服务器的命令连接和订阅连接。在一般情况下,Sentinel 以每十秒一次的频率向被监视的主服务器和从服务器发送 INFO 命令,当主服务器处于下线状态,或者 Sentinel 正在对主服务器进行故障转移操作时,Sentinel 向从服务器发送 INFO 命令的频率会改为每秒一次。
- 对于监视同一个主服务器和从服务器的多个 Sentinel 来说,它们会以每两秒一次的频率,通过向被监视服务器的 _sentinel_:hello 频道发送消息来向其他 Sentinel 宣告自己的存在。每个 Sentinel 也会从 _sentinel_:hello 频道中接收其他 Sentinel 发来的消息,并根据这些消息为其他 Sentinel 创建相应的实例结构以及命令连接。Sentinel 只会与主服务器和从服务器创建命令连接和订阅连接,Sentinel 与 Sentinel 之间则只创建命令连接。
- Sentinel 以每秒一次的频率向实例(包括主服务器、从服务器、其他 Sentinel)发送 PING 命令,并根据实例对 PING 命令的回复来判断实例是否在线,当一个实例在指定的时长中连续向 Sentinel 发送无效回复时,Sentinel 会将这个实例判断为主观下线。
- 当 Sentinel 将一个主服务器判断为主观下线时,它会向同样监视这个主服务器的其它 Sentinel 进行询问,看它们是否同意这个主服务器已经进入主观下线状态。当 Sentinel 收集到足够多的的主观下线投票之后,它会将主服务器判断为客观下线,并发起一次针对主服务器的故障转移操作。
- 当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个 Sentinel 会进行协商,选举出一个领头 Sentinel[1],并由领头 Sentinel 对下线主服务器进行故障转移操作。
Redis Sentinel 搭建
Redis Sentinel 部署技巧及其环境
- 一个健壮的部署至少需要三个哨兵实例,并且使用奇数个 Sentinel。
- 三个哨兵实例应该放置在客户使用独立方式确认故障的计算机或虚拟机中,例如不同的物理机或不同可用区域的虚拟机。
- 哨兵配置文件中只需要配置主从复制中的主副本 ip 和端口即可,当主从进行切换时哨兵会自动修改哨兵配置文件中的主副本 ip 为新在主副本 ip。
由于本人没有这么多服务器,因此在一台机器上模拟一个 Redis Sentinel 集群。
角色 | IP 地址 | 端口号 |
---|---|---|
Redis Master | 127.0.0.1 | 6380 |
Redis Slave-01 | 127.0.0.1 | 6381 |
Redis Slave-02 | 127.0.0.1 | 6382 |
Redis Slave-03 | 127.0.0.1 | 6383 |
Redis Sentinel-01 | 127.0.0.1 | 26381 |
Redis Sentinel-02 | 127.0.0.1 | 26382 |
Redis Sentinel-03 | 127.0.0.1 | 26383 |
Redis Sentinel 安装指南
1、下载 Redis 服务软件包到服务器,解压后并编译安装。
1 | [root@VM_24_98_centos ~] |
2、设置 Redis 主服务器
a. 创建目录以及复制配置文件
1 | [root@VM_24_98_centos redis]# mkdir -p /usr/local/redis/redis-master/redis-6380 |
b. 设置 Redis Master 主服务器配置环境
1 | # 开启远程连接 |
c. 启动 Redis Master 主服务器
1 | [root@VM_24_98_centos redis-6380] |
d. 客户端测试连接
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-server /usr/local/redis/redis-master/redis-6380/redis.conf |
3、设置 Redis 从服务器
a. 创建目录以及复制配置文件
1 | [root@VM_24_98_centos redis]# mkdir -p /usr/local/redis/redis-slave/redis-6381 |
b. 设置 Redis Slave 从服务器配置环境
1 | # 开启远程连接 |
c. 启动 Redis Slave 从服务器
1 | [root@VM_24_98_centos redis-6381] |
d. 客户端测试连接
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-server /usr/local/redis/redis-slave/redis-6381/redis.conf |
e. 同理,从服务器 redis-6382、redis-6383 按照上面的步骤部署。
4、Redis Sentinel 部署
a. 创建目录以及复制配置文件
1 | [root@VM_24_98_centos redis]# mkdir -p /usr/local/redis/redis-sentinel/redis-26381 |
b. 设置 Redis Sentinel 哨兵服务器配置环境
1 | # 端口号 |
c. 启动 Redis Sentinel 哨兵服务器
1 | [root@VM_24_98_centos redis-26381] |
d. 客户端测试连接
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 26381 |
e. 同理,哨兵服务器 redis-26382、redis-26383 按照上面的步骤部署
f. 查看 Redis Master 主服务器连接状况
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 6380 -a foobared |
Redis Sentinel 场景测试
模拟场景:Redis Master 节点挂掉,查看 Redis 集群状态。
Step1、关掉 Master 节点
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 6380 -a foobared |
Step2、通过哨兵查看集群状态
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 26381 |
通过 Sentinel 信息可以看到,Master 节点已经自动切换到 6381 端口了,说明主节点挂掉后,6381 Slave 节点自动升级成为了 Master 节点。
通过 Sentinel 日志文件显示了 failover 的过程:
Step3、启动 6380 Redis 服务,然后查看节点角色,此时 6380 变成了 Slave,6381 为 Master 节点
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-server /usr/local/redis/redis-master/redis-6380/redis.conf |
Redis HA 实践(Redis Cluster)
Redis Cluster 概述
Redis 集群是 Redis 提供的分布式数据库方案,集群通过分片(sharding)而非一致性哈希(consistency hashing)来进行数据分享,并提供复制和故障转移功能。Redis Cluster,主要是针对海量数据 + 高并发 + 高可用的场景。Redis Cluster 支撑 N 个 Redis Master Node,每个 Master Node 都可以挂载多个 Slave Node。Redis Cluster 节点间采用 Gossip 协议[2]进行通信。
节点:一个 Redis 集群通常由多个节点(node)组成,连接各个节点的工作可以使用 CLUSTER MEET <ip> <port> 命令来完成,将各个独立的节点连接起来,构成一个包含多个节点的集群。向一个节点 node 发送 CLUSTER MEET 命令,可以让 node 节点与 ip 和 port 所指定的节点进行握手(handshake),当握手成功时,node 节点就会将 ip 和 port 所指定的节点添加到 node 节点当前所在的集群中。
槽指派:Redis 集群通过分片的方式来保存数据库中的键值对,集群的整数据库被分为 16384 个槽(slot),数据库中的每个键都属于这 16384 个槽的其中一个,集群中的每个节点可以处理 0 个或最多 16384 个槽。Redis 集群有固定的 16384 个 hash slot,对每个 key 计算 CRC16 值,然后对 16384 取模,可以获取 key 对应的 hash slot。当数据库中的 16384 个槽都有节点在处理时,集群处于上线状态(ok);相反地,如果数据库中任何一个槽没有得到处理,那么集群处于下线状态(fail)。
Redis Cluster 重点总结
- 节点通过握手来将其他节点添加到自己所处的集群当中。
- 集群中的 16384(2的14次方)个槽可以分别指派给集群中的各个节点,每个节点都会记录哪些槽指派给了自己,而哪些槽又被指派给其他节点。
- 节点在接到一个命令请求时,会先检查这个命令请求要处理的键所在的槽是否由自己负责,如果不是的话,节点将向客户端返回一个 MOVED 错误,MOVED 错误携带的信息可以指引客户端转向至正在负责相关槽的节点。
- 对 Redis 集群的重新分片工作是由 redis-trib 负责执行的,重新分片的关键是将属于某个槽的所有键值对从一个节点转移至另外一个节点。重新分片操作可以在线(online)进行,在重新分片的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。
- 如果节点 A 正在迁移槽 i 至节点 B,那么当节点 A 没能在自己的数据库中找到命令指定的数据库键时,节点 A 会向客户端返回一个 ASK 错误,指引客户端到节点 B 继续查找指定的数据库键。
- MOVED 错误表示槽的负责权已经从一个节点转移到了另外一个节点,而 ASK 错误只是两个节点在迁移槽的过程中使用的一种临时措施。
- Redis 集群中的节点分为主节点(master)和从节点(slave),其中主节点用于处理槽,而从节点用于复制主节点,并在主节点下线时,代替主节点继续处理命令请求。
- 集群中的节点通过发送和接收消息来进行通信,常见的消息包括 MEET、PING、PONG、PUBLISH、FAIL 五种。
Redis Cluster 与 Redis Sentinel 区别
- 哨兵模式监控权交给了哨兵系统,集群模式中是工作节点自己做监控。
- 哨兵模式发起选举是选举一个 leader 哨兵节点来处理故障转移,集群模式是在从节点中选举一个新的主节点,来处理故障的转移。
Redis Cluster 搭建
Redis Cluster 部署技巧及其环境
- Redis 集群至少需要 3 个节点,因为投票容错机制要求超过半数节点认为某个节点挂了该节点才是挂了,所以 2 个节点无法构成集群。
- 要保证集群的高可用,需要每个节点都有从节点,也就是备份节点,即三主三从,所以 Redis 集群至少需要 6 台服务器。
- Redis 5.0 开始不再使用 Ruby 搭建集群,而是直接使用客户端命令 redis-cli 来创建。
- 不支持多数据库空间,集群模式下只能使用 db0 空间。
由于资源有限,因此在一台机器上模拟一个 Redis Cluster。
角色 | IP 地址 | 端口号 |
---|---|---|
Redis Cluster-Master-01-6391 | 127.0.0.1 | 6391 |
Redis Cluster-Master-02-6393 | 127.0.0.1 | 6393 |
Redis Cluster-Master-02-6395 | 127.0.0.1 | 6395 |
Redis Cluster-Slave-01-6394 | 127.0.0.1 | 6394 |
Redis Cluster-Slave-02-6396 | 127.0.0.1 | 6396 |
Redis Cluster-Slave-03-6392 | 127.0.0.1 | 6392 |
Redis Cluster 安装指南
1、下载 Redis 服务软件包到服务器,解压后并编译安装。
1 | [root@VM_24_98_centos ~] |
2、设置 Redis Cluster 服务器
a. 创建目录以及复制配置文件
1 | [root@VM_24_98_centos ~]# mkdir -p /usr/local/redis/redis-cluster/redis-6391 |
b. 设置 Redis Cluster 服务器配置环境
1 |
|
c. 启动 Redis Cluster 服务器
1 | [root@VM_24_98_centos ~] |
d. 客户端测试连接
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-server /usr/local/redis/redis-cluster/redis-6391/redis.conf |
e. 同理,集群服务器 redis-6392、redis-6393 、redis-6394、redis-6395、redis-6396 按照上面的步骤部署
3、Redis 5.0 开始不再使用 ruby 搭建集群,而是直接使用客户端命令 redis-cli 来创建。
a. 创建顺序三主三从,前面三个是主后面三个是从。由于我们设置了redis集群的密码,所以要带上密码。
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-cli --cluster create 127.0 :6391 127.0 :6393 127.0 :6395 127.0 :6392 127.0 :6394 127.0 :6396 --cluster-replicas 1 -a foobared |
b. 客户端测试连接
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 6391 -a foobared |
Redis Cluster 场景测试
(1)模拟场景:Redis Cluster 中 某个 Master 节点挂掉,查看 Redis Cluster 状态。
Step1、关掉 Cluster-Master-6391 节点
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 6391 -a foobared |
Step2、查看集群状态
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 6393 -a foobared |
通过 CLUSTER NODES 信息可以看到,Cluster-Master-01-6391 主节点处于下线状态(fail),其 Cluster-Master-01-6391 节点的从节点 Cluster-Slave-01-6394 变为主节点;说明主节点挂掉后,6394 Slave 节点自动升级成为了 Master 节点。
Step3、启动 6391 Redis 服务,然后查看节点角色,此时 6391 变成了 Slave,6394 为 Master 节点
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-server /usr/local/redis/redis-cluster/redis-6391/redis.conf |
(2)模拟场景:为 Redis Cluster 添加一个新主(master)节点
Step1、按照上面的步骤新增一 Redis Cluster 服务器 Cluster-Master-04-6397
Step2、将 Cluster-Master-04-6397 节点加入 Redis Cluster 中(127.0.0.1:6391 为集群中任意可用的节点)
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-cli --cluster add-node 127.0 :6397 127.0 :6391 -a foobared |
Step3、为节点 Cluster-Master-04-6397 分配 slots(127.0.0.1:6391 为集群中任意可用的节点)
1 | [root@VM_24_98_centos redis-cluster]# /usr/local/redis/bin/redis-cli --cluster reshard 127.0.0.1:6391 -a foobared |
(3)模拟场景:为 Redis Cluster 某个 Master 节点添加 一个新从(slave)节点
Step1、按照上面的步骤新增一 Redis Cluster 服务器 Cluster-Slave-04-6398
Step2、将 Cluster-Slave-04-6398 节点加入 Redis Cluster 中(127.0.0.1:6391 为集群中任意可用的节点)
1 | // 这种方法随机为 6398 指定一个 master |
Step3、查看集群状态
1 | [root@VM_24_98_centos ~]# /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 6391 -a foobared |
参考博文
[1]. 一文搞懂 Raft 算法
[2]. Redis 哨兵模式实现主从故障互切换
[3]. Redis cluster tutorial
[4]. redis cluster 的Gossip协议介绍
注脚
[1]. 领头 Sentinel:Sentinel 系统选举领头 Sentinel 的方式是对 Raft 算法的领头选举方法的实现,Raft 算法是一个共识算法,是工程上使用较为广泛的强一致性、去中心化、高可用的分布式协议。
[2]. Gossip 协议:Gossip protocol 也叫 Epidemic Protocol(流行病协议),实际上它还有很多别名,比如:“流言算法”、“疫情传播算法” 等。Gossip 过程是由种子节点发起,当一个种子节点有状态需要更新到网络中的其他节点时,它会随机的选择周围几个节点散播消息,收到消息的节点也会重复该过程,直至最终网络中所有的节点都收到了消息。这个过程可能需要一定的时间,由于不能保证某个时刻所有节点都收到消息,但是理论上最终所有节点都会收到消息,因此它是一个“最终一致性协议”。
Redis 深度探险系列
- Redis 深度探险(一):那些绕不过去的 Redis 知识点
- Redis 深度探险(二):Redis 深入之道
- Redis 深度探险(三):Redis 单机环境搭建以及配置说明
- Redis 深度探险(四):Redis 高可用性解决方案之哨兵与集群