写点什么

火山引擎 ByteHouse 基于云原生架构的实时导入探索与实践

  • 2023-12-21
    北京
  • 本文字数:3826 字

    阅读完需:约 13 分钟

火山引擎ByteHouse基于云原生架构的实时导入探索与实践

更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群


随着企业降本增效、智能化数据决策需求的增强,传统的商业数据库已经难以满足和响应快速增长的业务诉求。在此背景下,云原生数据库成为大势所趋。云原生数据库基于云平台构建、部署和分发,具有高可用性、高性能、高可靠等特点,可以帮助企业更好地实现数据智能化决策。

近期,火山引擎 ByteHouse 技术专家受邀参加 DataFunCon2023(深圳站)活动,并以“火山引擎 ByteHouse 基于云原生架构的实时导入探索与实践”为题进行了技术分享。在分享中,火山引擎 ByteHouse 技术专家以 Kafka 和物化 MySQL 两种实时导入技术为例,介绍了 ByteHouse 的整体架构演进以及基于不同架构的实时导入技术实现。

架构整体的演进过程

分布式架构概述

ByteHouse 是基于社区 ClickHouse 数据分析管理系统(下文简称社区)来做的产品集成和开发。ClickHouse 在开源以后,因为其实时分析方面极致的性能表现在业界被追捧。目前其开源社区的 star 活跃度非常高,国内很多公司都有针对 ClickHouse 开源社区做的产品集成和上云服务。

由于 ClickHouse 是基于 OLAP 实时分析而生的列存的数据库,其本身是一个分布式数据库,加之其底层设计和实现让它在性能方面非常优秀,具体表现为单机可以达到每秒上亿行的读取速度以及 GiB 级的数据吞吐。由于社区官方不会做云服务的限制,所以社区开源的只是分布式架构。

社区的开源实现是一个经典的分布式架构。首先它是无中心的多节点集群,有分片(shard)的概念:每个集群有多个 shard,每个 shard 相互独立;集群内每张表的数据划分为不同子集存储在不同 shard 上。由于分布式架构具有数据分片和本地存储的特性,所以它具有天然的并发性且高吞吐的优势。

当然,分布式架构也有其明显缺陷。首先,当集群达到一定规模后,再小的节点故障率也会导致一定量的故障处理单,而本地存储的运维门槛加剧了故障处理成本,尤其对于单副本集群,节点故障甚至会导致丢数据的风险;其次,分布式架构的读写耦合导致查询和导入存在资源竞争的问题;另外,由于本地存储 reshuffle 功能的成本问题,分布式架构的扩容成本非常高,而且容易导致线上服务 IO 热点,进而影响整个集群的稳定性。最后,由于无中心化节点以及事务的缺失,一致性问题是目前社区最为人吐槽的缺陷。


分布式架构下的实时导入

我们再来了解一下社区分布式架构下的实时导入实现,这里以 Kafka 导入为例。由于分布式架构多 shard,每个 shard 可以独立消费一部分 topic partition,可以有天然的并发优势;每个 shard 内部可以再通过多线程并发执行消费任务,进一步提高消费并发;加上本地写入的优势,使得导入任务可以有很高的吞吐。

社区 Kafka 消费实现采用 high level 的消费模式。high level 消费任务完全由 broker 分配和 rebalance,基本无法对数据分配做控制,也就无法满足对数据分配有需求的业务场景;同时也难以保证数据均衡。针对这个问题,ByteHouse 在开始引入 ClickHouse 时就做了优化——实现了 low-level 消费模式,使得数据分布以及均匀性能够得到保障。

由于架构缺陷,分布式架构下的 Kafka 导入存在类似痛点。首先由于没有事务保证,无法保证一致性,消费只能做到 At-Least-Once 或者 At-Most-Once;其次,查询高峰会导致读写资源的竞争,从而造成消费堆积;当存在扩容需求的时候,数据分布会存在一些冲突。最后,由于中心节点缺失导致需要去每个独立节点排查问题,运维成本随着集群规模线性增长。


从分布式到云原生

基于以上痛点,ByteHouse 进行了业界主流的架构升级和演进——从分布式架构到云原生架构的改造。火山引擎 ByteHouse 云原生架构分为三层:

  • 第一层是服务接入层,负责服务接入以及状态管理,包括整体服务入口、所有元数据信息、事务实现等。

  • 第二层是执行计算层(Virtual WareHouse,以下简称 VW),设计为无状态执行层可以轻量级扩缩容;负责执行具体的查询和导入任务,由于查询和导入可以下发到不同 Virtual WareHouse 从而实现读写分离。

  • 第三层是数据存储层(VFS),支持远端 HDFS 存储以及对象存储等多种存储方式,实现了存算分离。

状态管理层有一个元数据管理组件叫做 Catalog service,这里存储了包括表的 schema 以及用户数据的所有元数据信息;另一个重要组件是 Server,它的功能是承接整个集群的服务入口,用户的查询需求都会在 Server 进行预处理;在查询具体计算执行阶段,由于 VFS 数据存储导致的读数据开销,ByteHouse 在计算层实现了 DISK cache 功能——将频繁查询的数据缓存到计算节点的 local disk,以避免频繁远端数据读取的性能损耗。

为了解决社区饱受吐槽的一致性问题,ByteHouse 设计和实现了 Transaction,几乎所有任务(包括所有用户查询请求、DDL 请求、导入请求、后台实时导入任务等)的执行都会封装在一个事务中执行,保证一致性和原子性。

由于架构的升级和演进,实时导入技术也在新架构下做了适配和优化。下面仍旧以 Kafka 导入为例,看看 ByteHouse 云原生新架构下的实时导入的实现。

当用户创建一张 Kafka 表消费时,集群会在 Server 上为这张表创建一个唯一的任务管理器:管理器负责获取 Kafka topic 的元信息,并根据用户配置的 consumer 数据将 topic-partition 均匀分配给每个 consumer 任务;然后将每个 consumer 任务调度到合适的 VW 节点执行。

在 VW 节点,每个 consumer 会从 Kafka 拉取自身分配到的 topic-partition 对应的数据,并将这些数据写入到 VFS;每次写入数据后,系统会通过事务将写入数据的元信息以及最新 Kafka offset 提交到 Catalog 中;然后重复执行下一轮。

这个过程确保了数据从 Kafka 到 ByteHouse 引擎的完整导入,包括元信息的获取、后台任务的调度、数据的拉取与写入,以及 offset 的管理。通过这种方式,系统能够持续不断地从 Kafka 拉取数据并导入到 ByteHouse 中,形成一个不断的导入的实时数据流,满足用户的实时写入需求。

下面的表格简单比较了不同架构下实时导入技术的功能支持。除了上述提到的优化和改进,ByteHouse 还自研了唯一键引擎,并从 bytehouse 的分布式架构开始支持,完全适配到云原生架构上,配合 Kafka low-level 消费模式,可以完美解决用户实时导入唯一键场景需求。同时,ByteHouse 云原生架构通过独立的事务实现,在实时导入上消费语义升级支持 Exactly-Once,满足了部分用户对数据准确性的要求。这些改进使得团队能够更好地满足用户的需求,提供更加稳定和高效的服务。


基于云原生架构上 Kafka 和 MySQL 的设计和实现

下面以 Kafka 和物化 MySQL 为例,简单分享一下 ByteHouse 云原生新架构下的实时导入技术的具体实现细节。

Kafka

如上文所述,在 Kafka 导入中,每个表在 Server 端都有一个对应的任务管理器 Manager,这是一个后台常驻线程,负责从 Kafka broker 中获取 topic 的元信息,并从 Catalog 中获取上次成功提交的 offset;然后,根据任务映射规则,将 partition 分配给不同的 consumer,并将最新的消费 offset 一同下发到 VW 节点进行执行。

每个下发的任务都是作为一个常驻线程处理的。一旦任务被调度,如果没有异常,它会不断循环执行:先消费一批数据,然后写入 ByteHouse;然后再消费下一批,直到上游停止操作或节点宕机。


此外,为了优化消费,我们引入了一个名为 memory buffer 的功能。这个功能是为了解决某些业务场景下对延时性要求较高的需求。比如用户需要在 1-2 秒内查询到数据,但 ClickHouse 的攒批消费设计实现方式很难做到如此低的数据延时——频繁地小批量写入会导致底层数据文件增长以及查询性能降低。因此,我们引入了 memory buffer 功能,将消费的数据先写入 wal 中(持久化存储),然后缓存在内存提供查询,当缓存足够量再一次性刷盘。这样既能提高查询性能,又能解决底层存储问题。



在 VW 上,每个消费任务的执行流程如下:首先,向 Server 端发起 RPC 请求,创建一个事务来进行消费;然后,根据分配的任务,从 topic 中 poll 数据;当消费足够量的数据,对数据进行处理和转换,写入 VFS;最后将写入 VFS 的数据元信息和对应的消费 offset 通过事务提交回 Server 端,完成一次消费流程。

MySQL:

物化 MySQL 是社区目前的一个实验性功能,它的作用是把用户的一个 MySQL 的数据库库同步到 ClickHouse 里来帮助用户做一些 OLAP 分析。

物化 MySQL 的同步原理比较简单,当创建同步任务的时候,初始化阶段会把这个库里需要同步的表的数据全量拉取;当然,这里会有一个加锁和快照的操作,用以记录全量同步的位置,后续增量数据同步会从这个位置开始,通过实时同步 MySQL 的 binlog 并进行回放来实现。

对于底层存储,因为 MySQL 的数据表都有唯一性的需求,所以应用上 ByteHouse 自研的唯一键引擎就可以完美匹配。BinLog 消费跟上文提到 Kafka 消费原理基本一致。MySQL 有一个 GTID 的功能,可以充当类似于 Kafka 的 offset 角色,配合 ByteHouse 云原生架构的事务功能,每次在回放完以后同步提交数据元信息以及对应的 GTID,保证做到不丢不重的消费导入。

目前 ByteHouse 支持的 MySQL 数据源包括官方的 MySQL 版本以及 AWS 和 GCP 这两个使用比较多的 rds,因为目前 ByteHouse 物化 MySQL 更多地服务一些海外的业务,在 AWS 以及 GCP 上会更普遍一下。具体支持的功能列表可以参见下图。


云原生架构和导入技术使用现状和未来规划


目前,火山引擎 ByteHouse 云原生架构已经全面服务内、外部多种业务场景,实时导入已支持超过 2500 个服务节点,每天实时导入数据规模超过 30PB,行数超过 10 万亿,每天的平均吞吐量是 350GB 每秒,算到每个消费线程大约 18MB 每秒。未来,火山引擎 ByteHouse 团队还将持续探索更通用的实时导入技术解决方案,进一步提升数据导入的性能和通用性,并持续推进开源社区建设。


点击跳转ByteHouse了解更多

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

小助手微信号:Bytedance-data 2021-12-29 加入

字节跳动数据平台团队,赋能字节跳动各业务线,对内支持字节绝大多数业务线,对外发布了火山引擎品牌下的数据智能产品,服务行业企业客户。关注微信公众号:字节跳动数据平台(ID:byte-dataplatform)了解更多

评论

发布
暂无评论
火山引擎ByteHouse基于云原生架构的实时导入探索与实践_数据库_字节跳动数据平台_InfoQ写作社区