写点什么

腾讯云 CDW-ClickHouse 云原生实践

  • 2022 年 4 月 01 日
  • 本文字数:4997 字

    阅读完需:约 16 分钟

腾讯云CDW-ClickHouse云原生实践

​1. 前言

开源列式数据库 ClickHouse 以极致的性能、超高的性价比获得了广泛好评。在 PB 级查询分析场景下 ClickHouse 是最佳解决方案之一。开源 ClickHouse 集群采用 SHARED-NOTHING 架构,增加计算节点非常容易。



图 1:开源 ClickHouse 架构

但是,开源 ClickHouse 也有明显的不足之处:

  • 采用存算一体架构,计算与存储耦合。存储与计算资源无法独立扩展。用户对计算与存储资源非对称需求越发强烈,并且希望云服务商能够提供更为灵活的资源编排能力。

  • 不具备弹性能力。开源 ClickHouse 集群没有数据均衡功能(rebalance)。在云托管 ClickHouse 阶段,通过业务层来均衡数据,代价大,耗时长。弹性效率十分低。

  • 运维成本高。开源 ClickHouse 运维成本高。例如,当集群扩容后,增量计算节点无法自动同步存量节点的 SCHEMA 信息,需要人工介入。

随着云原生理念深入人心,利用云原生架构对开源 ClickHouse 进行改造,计算资源池化,存储与计算分离,势在必行。业界对云原生 ClickHouse 并没有明确的定义。云原生 ClickHouse 至少需要具备以下特征:

  • 采用存算分离架构,计算资源与存储资源独立扩展,按需付费;

  • 高效弹性,计算资源扩容时数据 Zero-copy;

  • 计算资源池化,根据业务需求灵活编排计算资源;

  • 易运维,甚至免运维,只关注业务本身;

腾讯云数仓服务 CDW-ClickHouse 已从云托管演进为云原生服务,下文简称云原生 ClickHouse。本文叙述开源 ClickHouse 云原生改造过程中的难点、方案设计、关键技术、以及未来规划。

2. 云原生架构

为了解决开源 ClickHouse 的痛点,腾讯云 CDW-ClickHouse 采用了全新存算分离架构,将服务分为元数据服务层、计算层 和存储资源层。



图 2:云原生 ClickHouse 架构图

元数据服务层:元数据服务层包含集群管理节点 clickhouse-admin 以及 元数据持久化存储。元数据管理节点 clickhouse-admin 负责管理集群的元数据元数据包括集群的数据分布表、SCHEMA 信息等。同时,clickhouse-admin 还负责计算节点的保活功能。集群管理节点 clickhouse-admin 为无状态设计,可水平扩展。

计算层:云原生 ClickHouse 计算层由计算组构成,计算组为计算节点集合。在计算层计算节点可以分组隔离。部署 clickhouse-server 节点为无状态存在,数据存储剥离到分布式存储系统或对象存储之中。用户可以根据业务合理编排计算资源。

存储资源层:云原生 ClickHouse 对存储资源做了统一抽象,用户无需关注底层存储。架构层方便集成更多云上分布式存储服务。可以集成低成本无限容量的对象存储,也可以集成低延迟高吞吐的分布式文件系统。

目前,已经集成了对象存储 COS,以及分布式文件系统 CFS。

接下来章节分别对架构中各部分展开叙述。

3. 集群元数据管理

开源 ClickHouse 采用的是 SHARED-NOTHING 架构,节点之间并不同步一些关键的信息。用户向集群新增节点后,增量节点并不会自动从存量节点上继承 SCHEMA 信息,导致易用性极差。云原生 ClickHouse 引入 clickhouse-admin 角色,用于管理集群全局信息:

  • 数据分布表:共享存储中数据与计算节点映射关系数据。

  • SCHEMA 信息:ClickHouse 集群中的 schema 对象。

  • 配置信息:包括计算节点配置,共享存储配置,以及计算分组的配置等。

该角色为集群的管理节点,无状态设计,具体数据存储在持久化系统中。在具体部署中,clickhouse-admin 节点可以根据情况进行多节点部署,共同分担负载。

3.1 处理 DDL 请求

云原生 ClickHouse 引入了新的管理节点。为了简化部署,也为了方便集群元数据统一管理,clickhouse-admin 接管了 DDL 功能。云原生 ClickHouse 不再依赖 ZooKeeper 集群。



图 3:统一处理 DDL 请求示意图

如图 3 所示,在一个简单的部署环境中,DDL 请求执行流程。客户端发给 clickhouse-server 的请求,会转发到 clickhouse-admin。接着,clickhouse-admin 统一将请求分发到对应计算组的节点。当前请求完成后,结果再原路转发给客户端。

在 DDL 被接管后,集群 SCHEMA 信息会被统一管理。这是后续集群扩容,节点重启,确保 SCHEMA 一致的基础。

3.2 元数据分发

开源 ClickHouse 采用的 SHARED-NOTHING 架构。集群的节点之间并不同步元数据。当集群新增节点,或者节点重启后,节点无法获取到集群最新的 SCHEMA 信息。这也是 ClickHouse 用户的痛点之一。

云原生 ClickHouse 集群在节点启动时,会向 clickhouse-admin 注册服务。如果不属于该集群的节点,会注册失败。完成注册后,clickhouse-server 从 clickhouse-admin 获取最新的元数据,包括 schema 信息、数据分布表信息。随后,clickhouse-server 进入数据加载阶段。

计算节点 clickhouse-server 启动完成后,会与管理节点 clickhouse-admin 进行心跳同步。心跳信息中包含了元数据的版本信息。若版本变化,则同步更新最新元数据。从而在集群范围内,计算节点获取的元数据最终一致。



图 4:原数据分发:启动和心跳

元数据分发功能为弹性伸缩奠定基础。

3.3 失败节点检测

在云原生 ClickHouse 集群中,计算节点 clickhouse-server 与元数据管理节点 clickhouse-admin 之间保持心跳。clickhouse-admin 通过心跳信息踢出异常节点、以及感知新节点。

失败检测(Failure detector)通常采用心跳(Heartbeat)模型。在云原生 ClickHouse 的实现中,并没有单纯根据心跳超时来判断节点失效。由于网络延迟抖动,以及节点负载的变化,单纯根据超时来判断会有很大概率误判。参考 Cassandra 中失效检测模块的实现,算法细节见。

3.4 计算节点分组隔离

所谓计算组,就是一组计算节点的集合。也就是云原生 ClickHouse 集群支持部署多计算分组,满足业务按需资源编排。不同资源组可以共享相同数据,实现容灾以及读写分离功能。主要的应用场景:

  • 容灾:同集群中,将不同计算分组部署在不同可用区,实现计算层容灾;

  • 读写分离:可以规划 2 个计算分组,一个用户数据写入,一个用于数据的查询,避免数据写入与查询相互干扰。

  • 按业务需求合理编排计算资源:对于测试,可以分配小规模低配置计算组,对应重要业务分配更高配置计算资源。

目前,还不支持多计算组。这是云原生 ClickHouse 后续重要功能之一。

4. 自研表引擎

云原生 ClickHouse 中集成了自研表引擎:CloudMergeTree 以及 CloudDistributed。前者基于分布式存储系统或对象存储系统实现数据读写、后台合并、以及数据自均衡。后者职责为 ClickHouse 分布式数据写入以及查询。

云原生 ClickHouse 自研表引擎在与开源社区代码级兼容的前提下,具备如下能力:

  • 基于分布式存储系统/对象存储实现数据读写;

  • 接口保持语义兼容,完整支持 ClickHouse-SQL;

  • 以 Zero-Copy 方式实现数据重分布;

  • 弹性扩缩时有限影响服务;

虽是自研表引擎,在工程实践过程中,尽可能重用现有代码。关键逻辑都在 CloudMergeTree/CloudDistributed 中实现。之所以这样做,一个核心因素是为了保持云原生 ClickHouse 与开源 ClickHouse 能够同步升级。云原生的代码相对对立,不会耦合在开源 ClickHouse 现有逻辑里,从而能够确保兼容与升级。

4.1 统一存储视图

云原生 ClickHouse 的自研表引擎提供了统一的抽象视图,并不绑定在具体的分布式存储系统或者对象存储。



图 5 统一存储视图模型

统一抽象存储层屏蔽了底层物理层次的细节。一方面,极大简化了自研表引擎的逻辑;另一方面,方便集成更多分布式存储系统或者对象存储。

统一抽象存储层将底层存储系统划分为固定数量的逻辑单位,简称为桶(Bucket)。具体桶的数量,在创建表是指定。抽象存储层的桶会均衡的分布在计算组内计算节点中。桶与计算节点之间的映射关系,称为数据分布表,由管理节点维护。划分桶的核心原因在于,简化弹性伸缩时数据重分布:

  • 当扩容时,存量节点上的桶会重新分配给增量节点;

  • 当缩容,或节点被踢出时,一部分桶会重新分配给存量节点;

显然,弹性伸缩时,只有数据桶重新分布,出发数据加载与卸载,而没有数据复制。极大地提升了弹性效率。

4.2 数据重定向

在统一抽象存储视图前提下,为了更好支持 ClickHouse 分布式 JOIN 计算,用户可以通过 CloudDistribyted 的SHARD BY expr子句自定义表的数据分布。用户通过对参与 JOIN 的表作统一数据分布,可以实现 COLOCATE JOIN。

举例:

CREATE TABLE test ( `i` Int64, `s` String ) ENGINE = CloudMergeTree ORDER BY i
复制代码

基于表 test 创建分布式表。

CREATE TABLE test_dist ( `i` Int64, `s` String ) ENGINE = CloudDistributed('default', 'default', test) SHARD BY xxHash32(i) % 99
复制代码

与开源 ClickHouse 不同之处在于,创建分布式表时,可以指定数据分布的规则,即SHARD BY expr。如上例中,当数据写入时候,会评估每一行数据在xxHash32(i) % 99的值,该值与桶的总数量取模运算,获取桶 ID,数据将被分发到对应的桶中。

4.3 数据写入

云原生 ClickHouse 与开源 ClickHouse 写入流程类似,基于统一抽象存储层实现。

在开源 ClickHouse 中,同一个表在每一个节点上独立分配递增的 BLOCK ID(可以理解为逻辑时间戳,用于标记数据写入的时序关系)。很明显,拥有较大的 BLOCK ID 的数据要后于拥有较小 BLOCK ID 的数据。

在云原生 ClickHouse 中,同一个表为每一个桶中维护了递增的 BLOCK ID。这个 BLOCK ID 维护在管理节点中。正因为如此,一些后台任务(MERGE/MUTATE),是以桶为单位进行的。

为了避免弹性伸缩期间影响数据写入,每个节点写入数据时,需要满足如下要求:

  • 向数据桶提交数据全局有序;

  • 任意计算节点提交数据前,需要加载其他节点已提交数据(若有);

  • 持有数据桶的计算节点,需要周期性加载其他节点已提交的数据(若有);

为此,实现了数据多节点并发提交机制。由于多个节点并发向桶中提交数据(data part),每个节点看到的数据不一定是最新的。在具体实现中,节点会周期性检查是否有新的数据需要加载。从而确保任意节点写入数据的最终一致性。更重要的时,需要确保不同节点看到数据提交的顺序是一致的。



图 6 多节点并发写入以及冲突解决示意图

如图所示,节点 clickhouse-server-2 第一次提交数据时,它本地感知到的 ID 是 98,它用 ID 99 来向 clickhouse-admin 提交数据。clickhouse-admin 看到当前 ID 为 100(其中 ID99-100 的数据为 clickhouse-server-1 提交),因此拒绝了该节点的数据提交。clickhouse-server-2 启动加载流程,加载 ID99-100 的数据后,以 ID101 重新提交数据,这次由于没有其他节点成功提交,故提交成功。

计算节点提交数据时候,会携带本地维护的 ID。clickhouse-admin 收到提交请求后,若请求携带的 ID 落后于全局 ID,则拒绝提交。如果提交的 ID 与全局 ID 吻合,则接受提交。当提交拒绝时,说明其他节点已经提交了数据,需要加载这部分数据后,再用新的 BLOCK ID 来提交数据,直到提交成功。

当弹性伸缩时,数据分布表在不同节点上可能不一致。存在多个节点同时写入同一个桶的情况。多节点并发机制确保了弹性伸缩阶段写入数据的正确性。也就是说,弹性伸缩期并不影响集群数据写入。

并发写入机制是后续跨可用区容灾,计算组节点容灾的基础。

4.4 数据查询

云原生 ClickHouse 与开源 ClickHouse 查询流程类似,基于统一抽象存储层实现。

在弹性伸缩过程中,数据桶发生迁移,即从一个节点调整到另外一个节点。在这个过程中,集群中多个计算节点看到的数据桶分布表不一致。在不一致期间,分布式查询的结果一定是错误的。

为了避免将错误的结果返回给客户端,云原生 ClickHouse 执行分布式查询时,会检查数据分布表,若分布表不同,则抛出异常。客户端收到异常后,需要重试。



图 7 桶迁移示意图

如图所示,在数据加载阶段,也就是 T1 时间内,查询服务仍然走原有节点。而在数据桶生效或者释放阶段,也就是 T2 时间内,数据桶分布表不一致,在该时间段内拒绝查询。

由于数据桶生效或者释放是非常短的时间,数据均衡对数据查询服务影响时间控制在较小时间段内。

5. 高效弹性

云原生 ClickHouse 实现了存储与计算资源分离。在弹性能力方面:

  • 存储资源层:无论是分布式存储系统或对象存储,都天然具备弹性能力;

  • 计算资源层:计算节点分组隔离,可以动态增加或减少计算分组。计算分组内可以动态增加和减少计算节点。

云原生 ClickHouse 对存储资源弹性扩展时,不会附带任何计算资源成本。在计算资源弹性时,只存在数据桶的所属关系变迁,无数据复制,弹性效率极大提升。

6. 未来工作

目前,云原生 ClickHouse 已经具备做到完整的弹性伸缩能力。用户可以按需购买计算资源与存储资源。在运维方面,云原生 ClickHouse 依赖云上运维管控系统,为用户提供开箱即用的服务。

云原生 ClickHouse 与开源 ClickHouse 有明显区别:

未来工作包括:

  • 增强对象存储查询效率,包括冷热数据管理、缓存管理等。

  • 支持计算节点容灾,包括支持多计算组实现跨可用区容灾,也支持计算组内计算节点容灾。

  • 自研存储引擎支持 UPSERT 能力。

敬请期待。

发布于: 刚刚阅读数: 2
用户头像

还未添加个人签名 2020.06.19 加入

欢迎关注,邀您一起探索数据的无限潜能!

评论

发布
暂无评论
腾讯云CDW-ClickHouse云原生实践_Clickhouse_腾讯云大数据_InfoQ写作平台