一, 简介
Redis 是一个开源的 key value 存储系统, 受到了广大互联网公司的青睐. redis3.0 版本之前只支持单例模式, 在 3.0 版本及以后才支持集群, 我这里用的是 redis3.0.0 版本;
Redis 集群采用 P2P 模式, 是完全去中心化的, 不存在中心节点或者代理节点;
Redis 集群是没有统一的入口的, 客户端 (client) 连接集群的时候连接集群中的任意节点 (node) 即可, 集群内部的节点是相互通信的(PING-PONG 机制), 每个节点都是一个 Redis 实例;
为了实现集群的高可用, 即判断节点是否健康(能否正常使用),Redis-cluster 有这么一个投票容错机制: 如果集群中超过半数的节点投票认为某个节点挂了, 那么这个节点就挂了(fail). 这是判断节点是否挂了的方法;
那么如何判断集群是否挂了呢? -> 如果集群中任意一个节点挂了, 而且该节点没有从节点(备份节点), 那么这个集群就挂了. 这是判断集群是否挂了的方法;
那么为什么任意一个节点挂了 (没有从节点) 这个集群就挂了呢? -> 因为集群内置了 16384 个 slot(哈希槽), 并且把所有的物理节点映射到了这 16384[0-16383]个 slot 上, 或者说把这些 slot 均等的分配给了各个节点. 当需要在 Redis 集群存放一个数据 (key-value) 时, Redis 会先对这个 key 进行 crc16 算法, 然后得到一个结果. 再把这个结果对 16384 进行求余, 这个余数会对应 [0-16383] 其中一个槽, 进而决定 key-value 存储到哪个节点中. 所以一旦某个节点挂了, 该节点对应的 slot 就无法使用, 那么就会导致集群无法正常工作.
综上所述, 每个 Redis 集群理论上最多可以有 16384 个节点.
二, 三种集群模型
1, 主从模型
在主从复制中, 数据库分为两类: 主数据库 (master) 和从数据库(slave).
当 slave 启动后, 主动向 master 发送 SYNC 命令. master 接收到 SYNC 命令后在后台保存快照 (RDB 持久化) 和缓存保存快照这段时间的命令, 然后将保存的快照文件和缓存的命令发送给 slave.slave 接收到快照文件和命令后加载快照文件和缓存的执行命令.
为了在主节点子集发生故障或无法与大多数节点通信时保持可用, Redis Cluster 使用主从模型, 其中每个哈希槽具有从 1(主节点本身)到 N 个副本(N 个) -1 个其他从属节点). 在具有节点 A,B,C 的示例集群中, 如果节点 B 失败, 则集群将无法继续, 因为我们不再有办法为 5501-11000 范围内的哈希槽提供服务.
但是, 在创建集群 (或稍后) 时, 我们向每个主节点添加一个从属节点, 以便最终集群由作为主节点的 A,B,C 和作为从属节点的 A1,B1,C1 组成, 如果节点 B 发生故障, 系统将能够继续.
节点 B1 复制 B, 并且 B 发生故障, 群集会将节点 B1 提升为新的主节点, 并将继续正常运行.
但是请注意, 如果节点 B 和 B1 同时失败, 则 Redis Cluster 无法继续运行.
解决问题:
解决 Redis 单例下, 数据体量大与数据备份造成的性能瓶颈问题, Redis cluster 主从模型很好的解决这个问题. 可以将读写操作分离到不同 Redis 实例上, 提高系统的吞吐量
引入新的问题:
1, 配置重连问题
不同的 Redis 实例, 需要不同的 ip 和端口对应, 如果某个实例下线了, 需要重新更改配置进行重连
2, 故障转移问题
如果某个结点故障下线, 无法进行故障转移, 比如某个 master 下线, 对应的 slave 结点也只能进行读操作, 无法进行写操作, 替代不了 master 的功能.
特点如下:
主数据库可以进行读写操作, 当读写操作导致数据变化时会自动将数据同步给从数据库
从数据库一般都是只读的, 并且接收主数据库同步过来的数据
一个 master 可以拥有多个 slave, 但是一个 slave 只能对应一个 master
slave 挂了不影响其他 slave 的读和 master 的读和写, 重新启动后会将数据从 master 同步过来
master 挂了以后, 不影响 slave 的读, 但 Redis 不再提供写服务, master 重启后 Redis 将重新对外提供写服务
master 挂了以后, 不会在 slave 节点中重新选一个 master
缺点如下:
master 节点在主从模式中唯一, 若 master 挂掉, 则 Redis 无法对外提供写服务, 不具备高可用性.
2, 哨兵模型
Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance).
监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常.
提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知.
自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会进行选举, 将其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器.
解决问题:
Sentinel 哨兵模式, 确实实现自动故障切换. 提供稳定的服务, 解决主从模型引入的新问题.
未解决的问题:
在哨兵模式中, 仍然只有一个 Master 节点. 当并发写请求较大时, 哨兵模式并不能缓解写压力.
特点如下:
Sentinel 可以监控任意多个 Master 和该 Master 下的 Slaves.(即多个主从模式)
同一个哨兵下的, 不同主从模型, 彼此之间相互独立.
Sentinel 会不断检查 Master 和 Slaves 是否正常.
监控同一个 Master 的 Sentinel 会自动连接, 组成一个分布式的 Sentinel 网络, 互相通信并交换彼此关于被监视服务器的信息, 在 sentinel 网络中, 只要还有一个 sentinel 活着, 就可以实现故障切换. 当只有一个 sentinel 的时候, 如果这个 sentinel 挂掉了, 那么就无法实现自动故障切换了.
故障转移: 投票(半数原则), 当任何一个 Sentinel 发现被监控的 Master 下线时, 会通知其它的 Sentinel 开会, 投票确定该 Master 是否下线(半数以上, 所以 sentinel 通常配奇数个).
故障转移: 选举, 当 Sentinel 确定 Master 下线后, 会在所有的 Slaves 中, 选举一个新的节点, 升级成 Master 节点. 其它 Slaves 节点, 转为该节点的从节点.
原 Master 重新上线, 当原 Master 节点重新上线后, 自动转为当前 Master 节点的从节点.
3,Redis cluster 集群
从 Redis 3.0 之后版本支持 Redis Cluster 集群, Redis Cluster 采用无中心结构, 每个节点保存数据和整个集群状态, 每个节点都和其他所有节点连接.
目标:
高达 1000 个节点的高性能和线性可扩展性. 没有代理, 使用异步复制, 并且不对值执行合并操作.
可接受的写安全程度: 系统尝试 (以尽力而为的方式) 保留所有来自与大多数主节点连接的客户端的写操作. 通常, 有一些小窗口, 在这些小窗口中, 可能会丢失已确认的写入. 当客户端位于少数分区中时, 丢失已确认写入的 Windows 更大.
可用性: Redis Cluster 能够在大多数主节点可访问且每个不再可用的主节点上至少有一个可访问的从节点的分区中生存. 而且, 通过使用副本迁移, 不再由任何从属复制的主将从一个由多个从属覆盖的主接收一个.
高性能:
采用了异步复制机制, 向某个节点写入数据时, 无需等待其它节点的写数据响应.
无中心代理节点, 而是将客户端直接重定向到拥有数据的节点.
对于 N 个 Master 节点的 Cluster , 整体性能理论上相当于单个 Redis 的性能的 N 倍.
高可用:
采用了主从复制的机制, Master 节点失效时 Slave 节点自动提升为 Master 节点. 如果 Cluster 中有 N 个 Master 节点, 每个 Master 拥有 1 个 Slave 节点, 那么这个 Cluster 的失效概率为 1/(2*N-1), 可用概率为 1-1/(2*N-1).
高可扩展:
可支持多达 1000 个服务节点. 随时可以向 Cluster 中添加新节点, 或者删除现有节点. Cluster 中每个节点都与其它节点建立了相互连接
一致性:
Redis Cluster 无法保证强一致性. 实际上, 这意味着在某些情况下, Redis Cluster 可能会丢失系统认可给客户端的写入.
Redis Cluster 可能丢失写入的第一个原因是因为它使用异步复制. 这意味着在写入期间会发生以下情况:
您的客户写信给主 B.
主 B 向您的客户答复 "确定".
主机 B 将写操作传播到其从机 B1,B2 和 B3.
B 在回复客户端之前不会等待 B1,B2,B3 的确认, 因为这会对 Redis 造成延迟性的延迟, 因此, 如果您的客户端写了一些东西, B 会确认写, 但是在崩溃之前崩溃由于能够将写操作发送到其从属服务器, 因此一个从属服务器 (未接收到写操作) 可以升级为主服务器, 从而永远丢失该写操作.
特点如下:
所有的 Redis 节点彼此互联(PING-PONG 机制), 内部使用二进制协议优化传输速度和带宽.
节点的 fail 是通过集群中超过半数的节点检测失效时才生效.
客户端与 Redis 节点直连, 不需要中间 Proxy 层, 客户端不需要连接集群所有节点, 连接集群中任何一个可用节点即可.
Redis Cluster 把所有的物理节点映射到[0-16383] slot(哈希槽) 上(不一定是平均分配),Cluster 负责维护 node <-> slot <-> value.
Redis 集群预分好 16384 个哈希槽, 当需要在 Redis 集群中放置一个 key-value 时, 根据 CRC16(key) mod 16384 的值, 决定将一个 key 放到哪个桶中.
三, 集群主要组件
1, 密钥分配模型
密钥空间被划分为 16384 个插槽, 有效地设置了 16384 个主节点的群集大小的上限(但是建议的最大节点大小约为 1000 个节点).
群集中的每个主节点都处理 16384 个哈希槽的子集. 当没有正在进行的集群重新配置时 (即哈希槽从一个节点移动到另一个节点), 该集群是稳定的. 当群集稳定时, 单个哈希槽将由单个节点提供服务(但是, 服务节点可以具有一个或多个从属设备, 在发生网络分裂或故障的情况下可以替换该从属设备, 并且可以用于扩展) 可接受过时数据的读取操作).
键映射到哈希槽的基本算法: HASH_SLOT = CRC16(key) mod 16384
2, 键哈希标签
哈希标签是一种确保在同一哈希槽中分配多个密钥的方法. 这用于在 Redis Cluster 中实现多键操作.
为了实现哈希标签, 在某些情况下, 密钥的哈希槽以略有不同的方式计算. 如果密钥包含一个 "{...}" 图案仅之间子 {和}, 以获得散列时隙被散列. 但是, 由于可能存在多次出现{或} 算法由以下规则很好地指定:
如果键包含一个{字符.
如果有一个}字符的右{
AND 如果在的第一次出现{和的第一次出现之间存在一个或多个字符}.
3, 集群节点属性
每个节点在集群中都有唯一的名称. 节点名称是 160 位随机数的十六进制表示形式, 是在节点首次启动时获得的(通常使用 / dev / urandom). 节点将其 ID 保存在节点配置文件中, 并将永久使用相同的 ID, 或者至少在系统管理员未删除节点配置文件或通过 CLUSTER RESET 命令请求硬重置的情况下使用该 ID.
节点 ID 用于标识整个集群中的每个节点. 给定节点可以更改其 IP 地址, 而无需也更改节点 ID. 群集还能够检测 IP / 端口的更改, 并使用在群集总线上运行的八卦协议进行重新配置.
节点 ID 并不是与每个节点关联的唯一信息, 而是唯一始终全局一致的信息. 每个节点还具有以下关联的信息集. 一些信息与该特定节点的集群配置详细信息有关, 并且最终在整个集群中保持一致. 某些其他信息 (例如上次对节点执行 ping 操作) 则是每个节点本地的.
每个节点都维护着有关群集中其他节点的以下信息: 节点 ID, 节点的 IP 和端口, 一组标志, 如果将节点标志为 slave, 则该节点的主节点是什么? 对节点执行 ping 操作, 最后一次接收到 pong 时, 将显示该节点的当前配置时期(在本规范的后面部分进行说明), 链接状态以及最终服务的哈希槽集.
4, 集群总线
每个 Redis Cluster 节点都有一个额外的 TCP 端口, 用于接收来自其他 Redis Cluster 节点的传入连接. 此端口与用于从客户端接收传入连接的普通 TCP 端口处于固定偏移量. 要获得 Redis Cluster 端口, 应在常规命令端口中添加 10000. 例如, 如果 Redis 节点正在端口 6379 上侦听客户端连接, 则群集总线端口 16379 也将打开.
节点到节点的通信仅使用群集总线和群集总线协议进行: 群集协议是由不同类型和大小的帧组成的二进制协议. 未公开记录集群总线二进制协议, 因为它不打算供外部软件设备使用该协议与 Redis Cluster 节点通信. 但是, 您可以通过阅读 Redis Cluster 源代码中的 cluster.h 和 cluster.c 文件来获取有关集群总线协议的更多详细信息.
5, 集群拓扑
Redis Cluster 是一个完整的网格, 其中每个节点都使用 TCP 连接与其他每个节点连接.
在 N 个节点的群集中, 每个节点都有 N-1 个传出 TCP 连接和 N-1 个传入连接.
这些 TCP 连接始终保持活动状态, 并且不会按需创建. 当节点希望对集群总线中的 ping 做出回应时, 会在等待足够长的时间以将节点标记为不可访问之前进行 Pong 响应, 它将尝试通过从头开始重新连接来刷新与该节点的连接.
虽然 Redis Cluster 节点形成一个完整的网格, 但是节点使用八卦协议和配置更新机制以避免在正常情况下在节点之间交换太多消息, 因此交换的消息数量不是指数级的.
6, 节点握手
节点始终接受群集总线端口上的连接, 即使收到 ping 节点不受信任, 甚至会在收到 ping 时回复 ping. 但是, 如果不将发送节点视为群集的一部分, 则接收节点将丢弃所有其他数据包.
一个节点仅以两种方式将另一个节点作为群集的一部分:
节点是否向其显示 MEET 消息. Meet https://redis.io/commands/ping 消息与 PING 消息完全一样, 但是会强制接收者接受该节点作为群集的一部分. 只有系统管理员通过以下命令请求节点时, 节点才会将 MEET 消息发送到其他节点:
群集会议 IP 端口
如果已经受信任的节点将闲聊该节点, 则该节点还将另一个节点注册为群集的一部分. 因此, 如果 A 知道 B,B 知道 C, 则最终 B 会向 A 发送有关 C 的八卦消息. 发生这种情况时, A 会将 C 注册为网络的一部分, 并尝试与 C 连接.
这意味着只要我们在任何连接图中加入节点, 它们最终将自动形成完全连接图. 这意味着群集能够自动发现其他节点, 但前提是存在系统管理员强制建立的信任关系.
这种机制使群集更加健壮, 但可以防止更改 IP 地址或其他与网络相关的事件后, 不同的 Redis 群集意外混合.
四, 重定向与重新分片
1, 移动重定向
Redis 客户端可以自由地向集群中的每个节点 (包括从节点) 发送查询. 节点将分析查询, 如果可接受(即, 查询中仅提及单个键, 或者提及的多个键全部位于同一哈希槽中), 它将查找哪个节点负责哈希槽一个或多个键所属的位置.
如果哈希槽由节点提供服务, 则查询将被简单地处理, 否则节点将检查其内部哈希槽到节点的映射, 并通过 MOVED 错误回复客户端
客户端需要将查询重新发出到指定节点的 IP 地址和端口. 请注意, 即使客户端在重新发出查询之前等待了很长时间, 并且与此同时, 群集配置已更改, 如果哈希槽 3999 现在由另一个节点提供服务, 则目标节点将再次发出 MOVED 错误答复. 如果联系的节点没有更新的信息, 则会发生相同的情况.
2, 集群实时重新配置
Redis Cluster 支持在集群运行时添加和删除节点的功能. 添加或删除节点被抽象为相同的操作: 将哈希槽从一个节点移动到另一个节点. 这意味着可以使用相同的基本机制来重新平衡群集, 添加或删除节点等等.
要将新节点添加到群集, 请将一个空节点添加到群集, 并将某些哈希槽集从现有节点移至新节点.
为了从群集中删除节点, 将分配给该节点的哈希槽移至其他现有节点.
为了重新平衡群集, 在节点之间移动一组给定的哈希槽.
该实现的核心是能够移动哈希槽的能力
3,ASK 重定向
从客户端的角度来看, ASK 重定向的完整语义如下:
如果收到 ASK 重定向, 则仅发送已重定向到指定节点的查询, 而继续将后续查询发送到旧节点.
使用 ASKING 命令启动重定向的查询.
尚未更新本地客户端表以将哈希槽 8 映射到 B.
一旦哈希槽 8 迁移完成, A 将发送 MOVED 消息, 并且客户端可以将哈希槽 8 永久映射到新的 IP 和端口对. 请注意, 如果有错误的客户端较早执行地图, 则这不是问题, 因为它不会在发出查询之前发送 ASKING 命令, 因此 B 将使用 MOVED 重定向错误将客户端重定向到 A.
参考链接:
官网: https://redis.io/topics/cluster-spec 集群规范