本文作者深入分析了 Redis 高可用的方方面面, 并且做了有效总结, 相信对广大读者可以起到很好的领路作用.
导读: Redis 是被广泛使用的基础软件之一. 对于工程师和, 架构师, 运维人员来说, 了解 Redis 的高可用方案和背后的原理, 是必备的基础知识. 本文作者深入分析了 Redis 高可用的方方面面, 并且做了有效总结, 相信对广大读者可以起到很好的领路作用.
作者 codedump codedump.info 博主, 多年从事互联网服务器后台开发工作. 可访问作者博客阅读 codedump 更多文章.
Redis 中为了实现高可用(High Availability, 简称 HA), 采用了如下两个方式:
主从复制数据.
采用哨兵监控数据节点的运行情况, 一旦主节点出现问题由从节点顶上继续进行服务.
主从复制
Redis 中主从节点复制数据有全量复制和部分复制之分.
旧版本全量复制功能的实现
全量复制使用 snyc 命令来实现, 其流程是:
从服务器向主服务器发送 sync 命令.
主服务器在收到 sync 命令之后, 调用 bgsave 命令生成最新的 rdb 文件, 将这个文件同步给从服务器, 这样从服务器载入这个 rdb 文件之后, 状态就会和主服务器执行 bgsave 命令时候的一致.
主服务器将保存在命令缓冲区中的写命令同步给从服务器, 从服务器执行这些命令, 这样从服务器的状态就跟主服务器当前状态一致了.
旧版本全量复制功能, 其最大的问题是从服务器断线重连时, 即便在从服务器上已经有一部分数据了, 也需要进行全量复制, 这样做的效率很低, 于是新版本的 Redis 在这部分做了改进.
新版本全量复制功能的实现
新版本 Redis 使用 psync 命令来代替 sync 命令, 该命令既可以实现完整全同步也可以实现部分同步.
复制偏移量
执行复制的双方, 主从服务器, 分别会维护一个复制偏移量:
主服务器每次向从服务器同步了 N 字节数据之后, 将修改自己的复制偏移量 + N.
从服务器每次从主服务器同步了 N 字节数据之后, 将修改自己的复制偏移量 + N.
复制积压缓冲区
主服务器内部维护了一个固定长度的先进先出队列做为复制积压缓冲区, 其默认大小为 1MB.
在主服务器进行命令传播时, 不仅会将写命令同步到从服务器, 还会将写命令写入复制积压缓冲区.
服务器运行 ID
每个 Redis 服务器, 都有其运行 ID, 运行 ID 由服务器在启动时自动生成, 主服务器会将自己的运行 ID 发送给从服务器, 而从服务器会将主服务器的运行 ID 保存起来.
从服务器 Redis 断线重连之后进行同步时, 就是根据运行 ID 来判断同步的进度:
如果从服务器上面保存的主服务器运行 ID 与当前主服务器运行 ID 一致, 则认为这一次断线重连连接的是之前复制的主服务器, 主服务器可以继续尝试部分同步操作.
否则, 如果前后两次主服务器运行 ID 不相同, 则认为是完成全同步流程.
psync 命令流程
有了前面的准备, 下面开始分析 psync 命令的流程:
如果从服务器之前没有复制过任何主服务器, 或者之前执行过 slaveof no one 命令, 那么从服务器就会向主服务器发送 psync ? -1 命令, 请求主服务器进行数据的全量同步.
否则, 如果前面从服务器已经同步过部分数据, 那么从服务器向主服务器发送 psync <runid> <offset > 命令, 其中 runid 是上一次主服务器的运行 id,offset 是当前从服务器的复制偏移量.
前面两种情况主服务器收到 psync 命令之后, 会出现以下三种可能:
主服务器返回 + fullresync <runid> <offset > 回复, 表示主服务器要求与从服务器进行完整的数据全量同步操作. 其中, runid 是当前主服务器运行 id, 而 offset 是当前主服务器的复制偏移量.
如果主服务器应答 + continue, 那么表示主服务器与从服务器进行部分数据同步操作, 将从服务器缺失的数据同步过来即可.
如果主服务器应答 - err, 那么表示主服务器版本低于 2.8, 识别不了 psync 命令, 此时从服务器将向主服务器发送 sync 命令, 执行完整的全量数据同步.
哨兵机制概述
Redis 使用哨兵机制来实现高可用(HA), 其大概工作原理是:
Redis 使用一组哨兵 (sentinel) 节点来监控主从 Redis 服务的可用性.
一旦发现 Redis 主节点失效, 将选举出一个哨兵节点作为领导者(leader).
哨兵领导者再从剩余的从 Redis 节点中选出一个 Redis 节点作为新的主 Redis 节点对外服务.
以上将 Redis 节点分为两类:
哨兵节点(sentinel): 负责监控节点的运行情况.
数据节点: 即正常服务客户端请求的 Redis 节点, 有主从之分.
以上是大体的流程, 这个流程需要解决以下几个问题:
如何对 Redis 数据节点进行监控?
如何确定一个 Redis 数据节点失效?
如何选择出一个哨兵领导者节点?
哨兵节点选择新的主 Redis 节点的依据是什么?
以下来逐个回答这些问题.
三个监控任务
哨兵节点通过三个定时监控任务监控 Redis 数据节点的服务可用性.
info 命令
每隔 10 秒, 每个哨兵节点都会向主, 从 Redis 数据节点发送 info 命令, 获取新的拓扑结构信息.
Redis 拓扑结构信息包括了:
本节点角色: 主或从.
主从节点的地址, 端口信息.
这样, 哨兵节点就能从 info 命令中自动获取到从节点信息, 因此那些后续才加入的从节点信息不需要显式配置就能自动感知.
向__sentinel__:hello 频道同步信息
每隔 2 秒, 每个哨兵节点将会向 Redis 数据节点的__sentinel__:hello 频道同步自身得到的主节点信息以及当前哨兵节点的信息, 由于其他哨兵节点也订阅了这个频道, 因此实际上这个操作可以交换哨兵节点之间关于主节点以及哨兵节点的信息.
这一操作实际上完成了两件事情: * 发现新的哨兵节点: 如果有新的哨兵节点加入, 此时保存下来这个新哨兵节点的信息, 后续与该哨兵节点建立连接. * 交换主节点的状态信息, 作为后续客观判断主节点下线的依据.
向数据节点做心跳探测
每隔 1 秒, 每个哨兵节点向主, 从数据节点以及其他 sentinel 节点发送 ping 命令做心跳探测, 这个心跳探测是后续主观判断数据节点下线的依据.
主观下线和客观下线
主观下线
上面三个监控任务中的第三个探测心跳任务, 如果在配置的 down-after-milliseconds 之后没有收到有效回复, 那么就认为该数据节点 "主观下线(sdown)".
为什么称为 "主观下线"? 因为在一个分布式系统中, 有多个机器在一起联动工作, 网络可能出现各种状况, 仅凭一个节点的判断还不足以认为一个数据节点下线了, 这就需要后面的 "客观下线".
客观下线
当一个哨兵节点认为主节点主观下线时, 该哨兵节点需要通过 "sentinel is-master-down-by addr" 命令向其他哨兵节点咨询该主节点是否下线了, 如果有超过半数的哨兵节点都回答了下线, 此时认为主节点 "客观下线".
选举哨兵领导者
当主节点客观下线时, 需要选举出一个哨兵节点做为哨兵领导者, 以完成后续选出新的主节点的工作.
这个选举的大体思路是:
每个哨兵节点通过向其他哨兵节点发送 "sentinel is-master-down-by addr" 命令来申请成为哨兵领导者.
而每个哨兵节点在收到一个 "sentinel is-master-down-by addr" 命令时, 只允许给第一个节点投票, 其他节点的该命令都会被拒绝.
如果一个哨兵节点收到了半数以上的同意票, 则成为哨兵领导者.
如果前面三步在一定时间内都没有选出一个哨兵领导者, 将重新开始下一次选举.
可以看到, 这个选举领导者的流程很像 raft 中选举 leader 的流程.
选出新的主节点
在剩下的 Redis 从节点中, 按照以下顺序来选择新的主节点:
过滤掉 "不健康" 的数据节点: 比如主观下线, 断线的从节点, 五秒内没有回复过哨兵节点 ping 命令的节点, 与主节点失联的从节点.
选择 slave-priority(从节点优先级)最高的从节点, 如果存在则返回不存在则继续后面的流程.
选择复制偏移量最大的从节点, 这意味着这个从节点上面的数据最完整, 如果存在则返回不存在则继续后面的流程.
到了这里, 所有剩余从节点的状态都是一样的, 选择 runid 最小的从节点.
提升新的主节点
选择了新的主节点之后, 还需要最后的流程让该节点成为新的主节点:
哨兵领导者向上一步选出的从节点发出 "slaveof no one" 命令, 让该节点成为主节点.
哨兵领导者向剩余的从节点发送命令, 让它们成为新主节点的从节点.
哨兵节点集合会将原来的主节点更新为从节点, 当其恢复之后命令它去复制新的主节点的数据.
如果过程中哨兵领导者失效怎么办?
来源: http://news.51cto.com/art/201905/596428.htm