Skip to content

Kvrocks 集群方案简介

Wang Yuan edited this page Feb 17, 2022 · 13 revisions

1 背景

为了让 Kvrocks 支持更大的数据规模,大家通常采用 pre-sharding 方式,将 Kvrocks 划分为一主多从的多个复制组,然后使用 Twemproxy 进行数据路由,通过 Redis Sentinel 实现高可用。该方案虽然简单,但很难实现集群的在线扩缩容和高效的集群管理,所以 Kvrocks 迫切需要实现自身的集群方案。

业界常用 Redis 集群方案主要有两类:类似 Codis 中心化的集群架构和社区 Redis Cluster 去中心化的集群架构。Codis 中心化方案需要 Proxy 进行请求路由,增加了额外的网络通讯成本和延迟,并且 Proxy 本身也有资源开销和运维成本; Redis Cluster 方案最大的问题是集群规模受限于其 Gossip 通信的开销而不能过大,而且去中心化架构中集群拓扑的可控性和运维性相对较差。

在 Kvrocks 的设计中,我们希望能够提供一种兼容两者优点的方案,既可以有 Redis Cluster 方案中不需要额外 Proxy 即可让客户端或 SDK 直接请求 Server的能力,又可以让集群规模无限扩展。

2 方案简介

在 Kvrocks 的 Cluster 方案中,每个 Server 节点可以像 Redis Cluster 节点一样提供集群拓扑结构,让 Redis Cluster 的客户端和 SDK 可以直接工作在 Kvrocks 集群上;每个节点集群拓扑结构的管理并不是由节点本身完成的,而是由外部的管控组件进行设置的,同时还避免了基于 Gossip 协议构建集群的复杂度(Redis 社区经过多年的努力才让基于 Gossip 协议的 Cluster 方案比较完善)。

Kvrocks cluster

2.1 拓扑结构管理

Kvrocks 提供了 CLUSTERX SETNODES 命令设置拓扑结构,需要注意的是,由于集群中的节点不会相互进行通信,所以该命令中的集群拓扑是整个集群的拓扑结构,而且也需要对集群中的所有节点都得执行拓扑结构的设置。设置拓扑结构的命令如下:

命令格式:

CLUSTERX SETNODES $ALL_NODES_INFO $VERSION FORCE

  • $ALL_NODES_INFO 为拓扑结构,格式为: $node_id $ip $port $role $master_node_id $slot_range;

    • $node_id:40个字节的字符串,是节点在集群中的唯一标识
    • $ip $port:节点的 ip 和监听端口
    • $role: 节点的角色,master 或者 slave
    • $master_node_id: 如果节点的角色是从库,则该值为主库的 node id,若为主库,则该值为 -
    • $slot_range: 该节点负责的 slot,支持范围和单个值,比如 0-100 200 205 这种格式,表明该节点负责0-100 以及第 200 和 205 的 slot。

    注意:每一行表示一个节点的信息,多个节点需要多行,通过\n(换行符) 分割多个节点信息

  • $VERSION: 新拓扑结构的版本号,为了避免拓扑被无序的错误修改,只有当新的拓扑结构版本号大于当前拓扑结构的版本号时才能更新集群拓扑结构。

  • FORCE: 强制更新,默认不设置,即验证版本号,不强行更新。添加 force flag 用于在集群拓扑结构管理混乱或出错时,强行设置集群拓扑结构。命令中带有 force flag 时 Kvrocks 不会进行 VERSION 校验,直接更新拓扑结构。

示例:

CLUSTERX SETNODES "67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 127.0.0.1 30002 master - 5461-10922 16380 16383
    07c37dfeb235213a872192d90877d0cd55635b91 127.0.0.1 30004 slave 67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1" 1

该命令设置的集群拓扑中包含了两个节点,其中 67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 节点为主库,ip:port 为 127.0.0.1:30002,负责的 slot 为 5461-10922 以及 16380 和 16383。07c37dfeb235213a872192d90877d0cd55635b91 是从库,主库id为 67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1,即第一个节点。拓扑结构 version 为 1,且不强制更新拓扑(没有 force flag)。

Kvrocks 执行 CLUSTERX SETNODES 命令设置拓扑结构,尽管可以通过比较节点的 ip:port 和自身监听的 ip:port 就可以确定自身节点 node id,然后知道自己的信息(node id,角色,负责的slot)。但我们可能将 ip 绑定为 0.0.0.0 而导致无法识别自身节点,所以 Kvrocks 提供了 CLUSTERX SETNODEID 的命令来设置当前节点的 node id,让该节点能够明确地获得其拓扑信息。

命令格式

CLUSTERX SETNODEID $NODE_ID $NODE_ID: 为40字节的字符串,每一个节点的 node id,在一个集群中需具有唯一性。

2.2 管控节点

Kvrocks 集群拓扑结构的设置通过简单的几个命令即可完成配置,所以管控节点可以实现的非常简单,甚至一个脚本就可以实现,当节点发生变化时,对集群中所有节点再次更新一下拓扑结构即可。由于其 Kvrocks 自身的集群功能中不支持故障切换,所以需要有其他工具检查节点故障,然后再更新拓扑。对于小规模集群,这些工作甚至可以由运维同学完成,将拓扑结构记录在数据库中,每次变更时更新拓扑。对于大多数运维 Redis 的公司,一般都会有自己的集群管理方案,Kvrocks 提供的命令也能其方便地接入到对应的管理系统中。

Kvrocks 还提供了 CLUSTERX VERSION 命令,查看当前节点集群拓扑结构的版本号,用于对集群拓扑的正确性检查,如果管控节点发现集群拓扑的 version 较小或者 version 出错,管控节点应更新该节点保存的集群拓扑。

一个完善的 Kvrocks 集群方案,需要依赖管控节点提供集群节点的元数据管理,故障检测和故障切换等功能,Kvrocks 团队的同学正在开发一款 Kvrocks 的管控服务 Controller,方便大家进行集群的管理和运维。

2.3 客户端访问

由于 Kvrocks 的集群访问能力兼容 Redis Cluster 方案,所以大家可以使用 Redis Cluster SDK 访问 Kvrocks 集群。具体实现上,Kvrocks 支持 CLUSTER NODES 和 CLUSTER SLOTS 命令,而 Redis Cluster SDK 正是通过这些命令获得集群拓扑,进行请求路由分发的。

与 Redis Cluster 一样,如果 Kvrock 收到请求中 Key 对应的 slot,不属于该节点处理的 slot,Kvrocks 也会返回 MOVED $slot_id $ip:$port 错误,客户端需要将请求重定向到新目标节点。

如果在你有一款支持 Redis Cluster 的 Proxy,比如开源的 redis-cluster-proxy,Kvrocks 也是支持的。这样能够屏蔽后端集群架构,客户端可以直接访问一个入口。

2.4 部署和运维

当前 Kvrocks 管控服务 Contoller 仍在开发中,尚不能使用,需要大家自行实现对 Kvrocks 集群的管理。部署和管理一个集群的步骤可以如下:

  1. 部署多个独立的 Kvrocks 实例,用以组建 Kvrocks 集群
  2. 设计该 Kvrocks 集群的拓扑结构,产生如 2.1 中描述的集群拓扑结构
  3. 对每一个 Kvrocks 实例,使用 CLUSTER SETNODEID 命令设置其节点 node id
  4. 对每一个 Kvrocks 实例,使用 CLUSTER SETNODES 命令设置集群拓扑
  5. Kvrocks 从库会根据拓扑结构中的信息,复制对应的主库,无需再设置主从关系
  6. 当节点有主从切换或需要增删节点时,需要重复 2-4 的步骤

当前,Kvrocks 集群拓扑结构的变更是基于全量状态进行转化的,也就是说每次拓扑结构的变更时,甚至只是一个节点的变更时,管控节点都需要会将全量的拓扑结构发送给每一个节点,这样的网络和 CPU 开销相对较大,但能够在不可靠的网络上尽可能保证拓扑结构的正确性,即使由于网络或管控自身原因导致中间部分更新没有被 Kvrocks 执行,也不会影响最终拓扑结构的正确性。因为我们设计了拓扑结构 Version 的概念,能保证拓扑结构在特定的基准上更新,这为设计基于操作的集群拓扑结构变更方案提供了基础,后续我们会提供添加节点、更新节点和删除节点等接口,方便运维。

此外,我们还可以通过 CLUSTERX VERSION 命令,用来查看当前节点集群拓扑结构的版本号,判断该节点保存的集群拓扑是否正确。

集群管理命令的安全性

为了保证客户端和 SDK 能够正确访问集群,通过我们无法重命名 CLUSTER 命令,但又担心客户端通过 CLUSTER 命令任意更改拓扑结构。在 Kvrocks 中,拓扑结构仍可以通过 CLUSTER 命令获得,但集群管理是通过 CLUSTERX 命令来完成的,所以可以通过重命名 CLUSTERX 命令来保证拓扑结构不会被任意更改。

*2.5 集群扩缩容

Kvrocks 的数据迁移是基于 slot 维度的,支持将一个 slot 上所有数据迁移到另外一个实例的,这跟 Redis Cluster 中数据迁移是基于 Key 维度的不一样,Kvrocks 作为 Redis 在大容量上的一个互补服务,其存储的数据量也比 Redis 大的多,按 Key 迁移的时间周期不可控。

管控节点或 DBA 通过 CLUSTERX MIGRATE 命令 进行 slot 迁移,即可完成一个特定 slot 数据的迁移。

命令格式

CLUSTERX MIGRATE $slot $dst_nodeid

  • $slot: 迁移的 slot
  • $dst_nodeid:数据迁移的目标 node 的 id

该命令会先迁移该 slot 的全量数据,再迁移增量数据,从而保证数据的正确性。对于 slot 数据迁移的设计和实现的详细内容,我们后续会有专门的文章进行介绍。

当 slot 数据迁移完成后,数据迁移的源节点会回复 MOVED 错误告知 SDK 需要重定向到目标节点。为了保证拓扑结构的正确性,Kvrocks 不会直接更改拓扑结构,需要管控节通过 CLUSTER SETSLOT 命令更改,当然也需要对集群中所有节点执行该命令。

命令格式

CLUSTERX SETSLOT $slot NODE $node_id $new_version

  • $slot: 需要重新设置归属的 slot
  • NODE: 跟 Redis cluster setslot 保存一致
  • $dst_nodeid:安排 slot 到的新节点 id
  • $new_version:新拓扑节点的版本号,注意该 version 必须为当前 version+1 时,也就是说管控中上一个版本的拓扑结构和节点当前的拓扑结构一致,Kvrocks 才会更新拓扑结构。因为该命令是基于操作的,所以必须保证基准数据是正确的,否则可能引起拓扑结构混乱。

先通过 CLUSTERX MIGRATE 命令迁移数据,再通过 CLUSTERX SETSLOT 命令更改拓扑结果即可完成一个 slot 的迁移。

3 总结

Kvrocks 实现了一种兼容 Redis Cluster 客户端的中心化集群管理方案,既能让 Redis Cluster 客户端可以直接访问集群节点从而避免额外的代理开销,又能通过中心化的方式让集群管理更简单更轻量。Kvrocks Cluster 的集群拓扑管理、集群访问、在线扩缩容等基本功能已完成,我们也会持续对 Kvrocks Cluster 的拓扑变更、可观测性和可运维性等方面进行优化,为大家提供更好的 Kvrocks 集群方案。

Kvrocks 社区计划为 Kvrocks Cluster 提供一个管控服务 Kvrocks Controller,从而更好地管理 Kvrocks 集群,该项目仍在开发中,也欢迎感兴趣的同学参与。