ByConity是基于 ClickHouse 产生。ClickHouse 社区已经发展了很多年。字节从 2017 年开始大规模使用 ClickHouse。在验证了多款产品之后,最终发现 ClickHouse 是唯一能够支撑字节当时的体量以及保证查询体验的产品。在使用 ClickHouse 过程中遇到了很多的痛点,比如说运维成本居高不下,扩缩容成本高,同时也有关联查询的局限性。
字节从 18 年开始对 ClickHouse 进行很多功能优化,但字节的业务发展比较快,无论是数据量还是业务的复杂度,上升得非常快。发展了两年之后发现简单的功能迭代可能已经难以满足业务发展需要,所以进行了整个架构重新的设计。当时从拥抱云原生架构的初衷,重新设计了存算分离架构,当时这个版本可以算是一个 ByConity 的雏形。这个版本之后,字节内部有很多业务从原先 MPP 架构向存算分离架构迁移。目前已经有一半以上的场景已经迁移到存算分离架构上来。在 22 年的时候,我们决定将这些优化回馈到社区。在与 ClickHouse 官方讨论之后,官方建议我们自己去开源产品。在 23 年 1 月份字节发布了开源的第一个版本 Beta 版。然后经历了大概半年的社区的反馈,以及本身功能的迭代后,我们在 23 年 5 月份正式发布了 GA 版本。现在 ByConity GA 版本已经发展了一年多时间。在这当中也陆续推出了几个小版本,像 0.2.0、0.3.0、0.4.0 等等。这些小版本当中,一方面是进行功能的迭代以及问题的修复,另一方面不停地进行性能的优化。
这里说下使用 ClickHouse 的一些痛点,ClickHouse 是一个典型的 MPP 架构数据库,它有很好的性能优势。比如说单表查询,尤其是聚合查询的时候,它拥有领先其他竞品的性能,但是使用它的时候也会有很多的痛点,其中一个最主要的痛点就是扩容成本非常高,因为本身架构的设计的原因,以及数据与计算节点捆绑的因素。我们每次进行扩容的时候,需要数据交进行重分布,会有很高的运维成本以及数据验证等资源方面的代价,对线上会是很大的影响。同时它很难平衡资源的有效利用,以及资源隔离。比如说如果希望一个业务能够与其他业务进行隔离,最好给他一个单独的集群,但是单独集群又很难把硬件资源很好地利用起来,因为当业务比较空闲的时候,这些资源就可能会浪费。
在读写方面也是一样,有时候如果大家都是读写同一份资源上,写请求一旦大了之后,就会很大影响线上的一些读请求,读写这块也没有办法很好的分离。同时 ClickHouse 另外一个比较痛的点是虽然它的单表查询性能非常好,但是在多表 join 这块是业界一直诟病的,没有办法做很高效多表查询,它的 join 优化效果非常差。所以我们在使用 ClickHouse 时,通常使用方法是在数据进入 ClickHouse 之前,进行很多的前期处理,会由 ETL 过程生成大宽表,然后把大宽表内容导入到 ClickHouse 后再进行查询。
ByConity是怎样解决这些痛点的呢?首先来介绍一下 ByConity 整个架构设计,ByConity 架构可以分为三层,最上面是共享服务层,中间是计算层,最下面存储层,先看最上面共享服务层,这层是整个集群所共享的公共资源节点,这些节点中有一组就是 ByConity 整个集群的入口,一组 Server 以及多个共享的服务,如 TSO,Resource Manager、Daemon Manager 等组成,同时这层有元数据存储,ByConity 有统一的中央元数据层。Server 承担的工作主要是 SQL 解析、生成执行计划,以及优化器对执行计划进行优化,同时它会跟元数据存储打交道,把元数据存储很好地管理起来,并且 Server 里有元数据的 Cache,所以可以加速元数据访问。这些共享服务中其中比较重要的一个是 TSO,它生成分布式事务所需要单调递增的时间戳,Resource Manager 主要管理 Workload 中间计算中所需要的资源,并进行资源分配的组件。Demon Manager 管理 Daemon。Daemon 类似 ClickHouse 里经常所需要做的 Merge task,在 ByConity 里也同样需要做。同时还有一些比较重要的后台进程,比如像 Kafka 的 Consumer,如果我们在进行实时数仓的数据导入,需要用到这样的组件,也是由 Daemon Manager 进行管理。
上面是服务层的组件,中间 Worker 层设计是无状态的,这是存量分离的比较重要因素。也就是说 worker 是可以弹性无感的扩缩容。为什么能做到呢?首先数据存放在远端的 CFS 层,远端数据层可以支持 HDFS 或者 S3 存储。Worker 查询会从远端存储把数据拿过来,然后在本地会有一定的基于磁盘的缓存进行加速,但这个缓存是由集群完全自动管理的,也不涉及到数据的一致性等,所以可以认为 Worker 是无状态的。同时引入了 Virtual Warehouse 的概念,一组 Worker 可以成为一个 Virtual Warehouse,资源隔离基本上基于这个概念。比如说架构里推荐的是做读写分类,也就是读和写 Load 需要建立不同的 Virtual warehouse 去做,这样的话就可以相当程度上能够让这个重的写操作不会去影响到在线读操作。同样在不同业务之间的资源隔离也可以用类似的方式去做。
上面介绍整个架构设计,再来说 ByConity 在功能的优化做了哪些。首先比较重要的就是解决了 ClickHouse 复杂查询这个大痛点。ByConity 对这种复杂多 Join 查询支持得非常好。为什么可以支持得非常好呢?这当中就需要涉及到整个查询 Pipeline 的改造,以及自研优化器的引入。整个计算层的改造主要是比如在 Server 改写了整个 ClickHouse 解释器,里面引入了 Query Planner 组件,Planner 可以生成 Query plan,然后 Query plan 经优化器优化,可以生成改写后的经过优化的 Query plan。每个 Query plan 是由多个 Plan Segment 组成。每个 Plan Segment 都可以下发到后面的计算层进行进一步的解析和计算。每一个 Plan segment 的计算可以使用类似原先 ClickHouse 的Pipeline schedule 的执行方式。在这个过程中就会涉及到自研的查询优化器,对 Query Plan 进行优化和改写。支持比较常见的优化手段,比如 RBO、CBO。RBO 是基于规则的,有基于像 Visitor 的改写方式,以及基于这种 Query patterner 的改写方式。基于 Visitor 的比如像谓词下推,列裁剪等。CBO 是基于 Cost,是用 Cascade 这种搜索框架对 Join 进行 Reorder。同时优化器还支持一些高级的优化手段,像 runtime filter,或者像 CT 的一些优化等等。
优化的效果也发过文章,在 ByConity 最初 GA 的时候,就对比了业界的一些比较流行的开源产品,在 TPC-DS 的标准数据集上跟这些产品做了 Benchmark,结果 ByContiy 整体性能非常好。可以在外面找到这些文章看 Benchmark 的细节。其实在 ByConity 开源并进行了一年迭代之后,也有了更多的性能优化,包括像 QPS 的优化、Projection 的优化等,这些优化会进一步提升性能。目前如果再重新做 TPC-DS Benchmark 会进一步提升。
除了本身的性能优化以外,ByConity 还有一些在真正生产使用当中所需要的非常重要的能力,比如说一致性。ClickHouse 另外一个比较痛的点就是它缺乏一致性,因为它本身是不支持事务的,它只能在一个有限的范围内支持一定的原子性 ACID 级别,但这在很多业务场景下是不够的。如果不支持事务,在很多时候一旦上游发生 fail,需要进行 recover 的时候,或者说数据有一定的异常,需要进行 Retry 等一些操作的时候,如果缺乏一致性,会对 OLAP 中的数据产生很大影响,也会影响到后续的业务分析以及后续系统的正确性。所以在 OLAP 系统中,事务和一致性也是比较重要的。在 ByConity 中是支持分布式事务的,并且能够支持到 Read committed 的隔离级别。怎么做到的呢?首先 ByConity 有统一的元数据层,这个元数据层是用 FoundationDB 开源组件实现的,FoundationDB 本身支持原子性操作,比如像 CAS,所以 ByConity 可以很好地利用 FoundationDB 的能力去实现分布式事务当中的一些比较重要的原子操作。另外就是引入了分布式的时钟,就是这个 TSO 组件可以为分布式事务提供单调递增的事务 ID。这是比较重要的分布式事务中的组件吧。因为这些组件的支持,可以实现典型的两阶段提交。在阶段一写入数据的时候,可以同时写入事务 ID,并且将 undo buffer 写入远端的存储并提交元信息。第二阶段是对这个事务真的进行提交,提交事务的时候,可以根据事务记录的提交时间进行处理。比如如果事务执行成功,可以真正更新 part 的提交时间,把 undo buffer 清理掉,并且把这个事务的记录点给清理掉。如果事务失败,需要进行一些回滚。那刚才记录的 undo buffer 就可以在回滚中发挥作用,首先把中间过程的 Part 数据删掉,因为失败它已经没有用了。根据 Undo buffer 把远端存储当中已经写了的数据进行回滚,同时在这些都处理好之后,再把 Undo buffer 和事务记录给处理掉,这就是一个非常典型的两阶段提交的分布事务操作。由于这样的支持,ByConity 在一致性上面是有很好的保证。
另外因为是存算分离架构,所以大家可能会比较担忧一些问题,比如说对远端存储的数据 IO 是不是真的能够满足查询的需要?所以在 ByConity 对冷读,也就是直接对远端存储读写的操作,做了很多的优化。冷读的性能无论如何都会比已经在 Disk Cache 当中缓存的数据的热读操作还是要慢。ByConity 一直在对这个过程进行优化,使得它对整个系统的查询影响尽量小。这些优化手段包括很多很细节优化,很多也是借鉴了操作系统的 IO 优化手段,以及其他分布式系统中的 IO 优化手段。下面介绍其中的一些。
比如 IO schedule 就是一个比较重要的组件,这个组件能够让 IO 请求的并发量比较大,并且请求所涉及到的数据比较接近的情况下,能够得到比较好的优化效果。首先会对 lO 请求进行一定的合并和分解,合并是针对比如两个 lO 请求比较小,但它其实是相邻的,或者说基本上相邻,中间可能隔了很少的一部分数据,那可以把这两个 IO 请求合并成一个。拆分是说有的时候一个 IO 请求非常大,它所涉及到的数据块非常大,如果直接进行请求的话,会有一个比较高的等待时间。所以在这种情况下,我们可以把它拆分成多个 lO 请求,然后采用并发方式去 load 数据。这两种情况在真正 IO 过程中其实都会有。
同时 ByConity 也会做一些 Prefetch 预读,尤其是在远端存储的单次 IO 请求比较高延,但是可以引入很多并发的情况下,它能够工作得非常好。另外在预读的时候,ByConity 引入了很多细节的考量,能够在 ClickHouse 的数据结构情况下使预读效果尽量的好。我们知道 ClickHouse 对远端数据的 IO 读取,是用 Mark range 作为单位的,在读取一个 Block 数据的时候,它会看 mark 的 range,在每个 mark range 中我们可以把数据分解成多个 task 去进行并行的读取。尤其在 S3 这样的存储时,可以去发很多的并行读的线程去同时做读取,在这种时候拆分成 task 是非常有效的。这些 task 分别去做 IO 的读取,同时在这个过程当中还会引入比如 task stealing 的操作,比如有的线程非常重的话,它所承的一些 task 可以被其他线程 steal。优化过程中也经过了很多迭代,比如实现的第一个版本发现 task 是按照 mark range 平均分布的。但发现效果不是特别的好,因为在这个当中有很多连续的数据块,还是可以稍微合并一下,所来实现了一个版本会把 Mark Range 进行动态分布,会根据读取数据的请求的 mark range 连续性去做。同时这些 task 也会被多个线程去分布式执行。同时我们也发现线程启动 task 的时候有一个比较长的启动时间,在这里也进行了很多异步化的处理,能够让启动时间尽量的少。经过一系列的冷读优化后,无论是从 HDFS 还是从 S3 去做冷读数据读取,都比最初的版本好很多。如果大家用到比较早期的 ByConity 版本,遇到冷读这个性能觉得有问题的话,可以尽快升级到最新的版本。
接下来介绍 ByConity 典型的使用场景。首先是字节内部的一些使用,介绍一个抖音集团做行为分析的场景。首先抖音的数据量是非常大,在这个场景中抖音的用户行为数据在经过埋点日志上报之后,它会在数仓层建模去存放这些埋点数据,以及有像用户画像的相关数据。这些数据有离线的,也有实时的。实时这块还是还在存算分离改造迁移过程中。但离线这块已经完全从原来存算一体的版本迁移到存算分离版本,也就是 ByConity 中。所以说抖音行为分析的离线分析,已经完全可以在 ByConity 里面做。它是对整个抖音的用户行为分析平台进行了很好的支撑。这个平台被称为>
因为 ByConity 是开源产品,有其他的公司基于 ByConity 做平台的实践。那这边介绍一个展心展力,基于 ByConity 的可观测平台的实践。可观测也是 OLAP 查询引擎的领域用的非常多的场景,那么展心展力的这个场景也是比较典型的。客户端上报的日志,以及有一些可观测的工具上报指标,通过实时链路传输到 OLAP 引擎中,然后再由 OLAP 引擎做作为计算层,对上面的BI 分析以及报表应用提供引擎的计算能力,展心展力在 OLAP 的迭代也有一个过程,最早是使用了 ES,后面换成了 ClickHouse,现在最终再换成 ByConity,所以这个也是从 ClickHouse 迁移到 ByConity 的一个非常典型的场景,迁移的效果也非常好。首先性能,因为 ByConity 性能在很多或大部分的查询场景下,对于 ClickHouse 是有优势的。尤其是查询相对比较复杂,或者说有一些比较特殊的场景,比如像 like 这样场景情况下,都有一些明显的性能优势。这些性能优势会导致做同样查询的流量,处理会比用原先用 ClickHouse 需要更少的资源,所以可以帮整个 OLAP 层节省很多的资源。展心展力的 CPU 和内存资源都得到了相当程度的节省。另外展心展力自己引入了另一个非常好的,称为定时扩缩容的方式机制。这个定时扩缩容的意思是可以在系统流量波峰的时候,把资源进行扩容,然后在流量波谷的时候再进行缩容,因为存算分离架构以及扩缩容无感的优势,使得扩缩容可以做得非常频繁,并且运维的代价非常低,所以这种方式也可以非常好的节省他们的成本。如果说本来这些资源是准备好的,那在现在,就可以进行动态的伸缩。
最后介绍 ByConity 将到来的版本 1.0。这个版本有一些重新的定位,以及一些新特性。首先说 ByConity 1.0 版本提供了三个核心的观念。首先就是一直秉持的业界领先的性能优势,因为 ByConity 性能,刚才也介绍了有很多优化手段。除了自研的优化器解决本身 ClickHouse 的多表 Join 的场景以外,在过去的一年的迭代当中也做了很多的优化,所以整体性能肯定是业界领先的。另外存算分离从设计之初时,整个架构设计就是秉持着云原生和存算分离的理念去进行设计的,称为整个业界最纯粹的存算分离的版本,整个 worker 层是真正无状态的,真正是可以无感扩缩容的。这是一直秉持的理念,另外一个是希望新引入的一个理念:完整的数据仓库。ByConity 1.0 不仅可以定位成一个 OLAP 层引擎,而且希望把它定位成一个完整的数仓,也就是说希望 ODS 层就可以从 ByConity 开始去建设,也要提供一系列的能力。能让数仓的建设不仅仅是能够做它的查询层,同时也可以做数据的处理,包括 ELT 能力。
首先是 ELT 能力,就是希望能够把 ByConity 定位成一个数仓引擎的非常重要的功能,ELT 能力是指在 ByConity 内部进能够进行一定的数据处理,能够进行长任务的支持,这个区别于之前 ClickHouse,它就是查询层引擎的定位。定位是查询层引擎的话,基本上所有的查询都是同步的,都是短时的,在这种情况下,它整个的计算层以及存储层设计就专门针对短时的查询进行优化,跟需要进行数据处理的长任务支持是完全不一样的。比如需要去进行长时任务的情况,首先需要异步化的改造,需要引入查询队列入。对 Server 以及 worker 里的很多能力进行了改造和扩充。比如在 server 引入了队列的 manager,以及维持多个队列的形式。在 worker 中引入了进行资源管控的一些模块,也结合 resource manage 能力,可对整个 worker 里的资源耗费情况进行很好的管控。同时也可以对整个查询的资源进行预估。在这种情况下就可以很好的去评估一个查询,它到底需要使用多少资源,目前这个资源够不够等?如果不够的话可以把它放到队列里面稍后去执行。如果够的话,采用哪些 worker、哪些资源去进行计算?整个这块进行了很好的改造和支持。同时也进行了整个执行模式的改造,从整个 MPP 执行模式改造成为 BSP 的模式。BSP 模式我们认为是分阶段的一个模式,它主要的理念是下一个阶段的执行都必须等到上一个阶段执行完全成功之后才会进行。跟原来 MPP 模式有很大的不一样。在 MPP 模式很多的并行计算任务以及资源会在一开始就 plan 好,并且分配好。分配好之后,当中很难去动态改变,当中如果出了问题,它也能只能从头开始重试,没有办法从中间的出问题的点开始重试。那 BSP 模式就可以很好地解决这个问题,这也是为什么能够支撑长时任务的非常重要的点。BSP 模式从执行的分阶段进行,除了这个支撑之外,它还需要可以通过磁盘进行数据的 shuffer 的一个非常重要的功能。这两个功能要能够很好地整合,我们才能够对 BSP 模式进行非常好的支持。基于磁盘数据 shuffer 是 ByConity 1.0 里面会引入的一个非常重要的能力。例如在一个 worker 内有一定数据计算结果之后,会把这些中间结果落地到 worker 的磁盘当中,并且 exchange manager 可以让其他的 worker 从磁盘中进行读取。这也是支持长时任务所要求必备的基于磁盘的 Exchange 能力。同时在 1.0 还会提供一些比较高级的能力,比如 Adaptive query execution 能力。AQE 其实是在进行 Spark 和 Flink 应用的时候都会遇到的比较高级的 Adaptive 的能力。在 ByConity 1.0 也会提供比如说像这种 parallelism 的自动调整的 AQE 能力。
另外一个比较重要的 1.0 提出来的是湖仓一体能力。湖仓一体是 1.0 希望能够用湖上建仓的方式去进行这个湖仓一体的建设。也是依赖 ByConity 1.0 提供出来的很多能力去进行的。在湖仓支持方面,ByConity 1.0 很好地对接到 Hive meta store,能够对 Hive meta store 的元数据进行很好的处理。除了能够直接读取元数据之外,还能够对元数据进行缓存以及进行自动的从 Hive Meta store 进行同步。除此以外,还对很多类型的外表做了支持,比如 Hive 的外表,以及 Hudi 格式的外表也做了很好的支持。在数据结构方面,对 Parquet 和 ORC 的数据结构都进行了很好的支持。同时也支持在 HDFS 和 S3 里进行存储支持。为了进一步加速数据湖的外表查询在仓里面的查询性能,引入了物化视图,支持了多种物化视图。除了基于单表的同步物化视图以外,还可以进行多表的异步物化视图的构建,同时也可以对外表进行物化视图,也就是对外表的查询的中间结果可以物化下来放在物化视图里,后面就自动的查询改写,查询请求直接通过访问物化视图的方式得到,不用真正去访问远端的湖里面的这些数据。同时也支持外表和内表能够进行关联查询,在很多湖上建仓的场景下也是非常普遍的。这样的话离线数据进湖,实时数据进仓,可以对实时数据和离线数据进行关联的查询。这些能力的支持就可以给融合数仓和数据湖的应用,提供了一个非常好的参考模式。后面还会进行进一步地迭代开发,在 1.0 之后,还会提供比如说数据湖的写入这样的能力。一些像仓下存湖的场景能够得到进一步的支持。
另外一个 1.0 里面比较重要的能力是倒排索引。倒排索引其实是 ClickHouse 社区本来一直在迭代、支持的能力。在 ByConity 里倒排索引能力进行了一定的增强。首先在分词方式方面,支持 token 分词,Mgram 分词。除此以外,还支持中文分词,引入中文的分词库,通过配置分词库去进行中文的分词。同时引入了词组匹配和相似度的检索,目前还在开发,在下一个版本 1.0 会真正上线这个能力。这些能力上线之后,整个倒排索引会使得像这种 like查询的场景下,性能得到非常大的提升。比如说查询耗时能够降低 3- 4 倍,同时对 L(load)的请求也能降低 4- 5 倍。这个场景大家会自然地联想到 elastic search,因为这个场景是 elastic search 的典型场景,那使用 ByConity 去做倒排索引的文本检索,对于 elastic search 有什么优势呢?首先它的整个架构设计,以及本身这个 OLAP 引擎性能的优势,写入的吞吐量是非常大的,同时查询性能也相比 elastic search 有更低的延时。这个是本身已经具有了的性能优势。同时因为存算分离的架构,可以做很好的资源隔离。所以负载可以做得更均衡,运维成本也可以做得更低。另外就是易用性,因为 ByConity 是一个基于 SQL 的数仓引擎,它的使用就是标准 SQL 语法。还支持了很多种不同的 SQL 方言,所以使用上面来讲比 elastic search 要易用很多。
最后介绍 1.0 推出后兼容性方面的支持,也就是 MySQL 兼容性。因为 ByConity 本身基于 ClickHouse,所以它对 ClickHouse 生态的支持以及 ClickHouse 的 SQL 的语法兼容性肯定是没有问题的。但是在很多数仓使用场景下,很多用户其实更希望能够通过 MySQL 的方式去使用数仓。或者说他们现在数仓使用场景里面就已经使用了 MySQL 很多的特性,以及已经有很多积累在 MySQL 上面,比如说 SQL 或者工具的使用等,都希望在 MySQL 的生态方面得到支持。所以在 1.0 版本对整个 MySQL 生态进行了全面的支持。那这个支持包括语法、函数、数据类型,包括 DQL、DML、DDL 等都进行了一一排查去支持它。整个的支持兼容度是大于 90% 的,对一些目前没有兼容的,也在会在 SQL 层里做很好的说明,说清楚可以有什么替代方法或者改写方法。基本上可以保证什么呢?就是已经使用了 MySQL 生态的工具的,比如说 BI 工具像 Tableau、quick BI、Fine BI 等等,或者说一些 IDE 工具,MySQL workbench, DBeaver, Navicat 等,通过这些工具能够无缝地使用和连接到 ByConity去做各种查询或者各种操作。同时一些典型的 driver,像 JDBC driver,走 MySQL 协议连进来也是可以的,都能够无缝支持没有什么问题,尽量保证使用的时候是可以兼容的。
接下来介绍 24 年整个迭代的路线。1.0 版本是下一个版本,这个版本可能会在 Q3 中,比如说 7 月底时间点发出来。整个一整年的迭代都会有各类功能的建设。其实上半年已经完成了刚才提到的湖仓能力、MySQL 兼容性、LT 以及全文检索的能力建设。这些能力也不是一下放出,而是在那之前,比如 0.2、0.3 等版本中陆续放出这些能力的功能点。之前是以功能点的形式在小版本中提供出来,在 1.0 版本会认为整个建设能够比较完备,是一个可以达到 GA 状态的版本,大家可以真正在生产环境下去比较完整地使用这些方面的能力。下半年还会对上述的这些领域进行进一步的支持,比如说在湖仓领域会支持 iceberg 以及 Hudi 和 iceberg 写入等。另外在性能这块也是一直秉持的,一定要做到业界最优性能这样主旨。所以在性能这块会一直不停地去做迭代,会有更多的性能提升点,像 Zero copy、一些系统的改造能够让性能得到进一步提升、分布式的缓存等。同时在生态这块也会进一步地支持,比如 ClickHouse,要一直是兼容它的生态,但是 ClickHouse 也在发展过程中。后续有更新版本的 ClickHouse,也会去考虑对它的新 SQL 进行一定支持。同时也吸收了自于社区的一些反馈,就像元数据,有很多社区小伙伴反馈说 Fundation DB 的运维是他们的痛点,Fundation DB 本身外面成型的生态及工具比较少。后面会在考虑对整个元数据进行重构,一方面看一下能不能减少元数据存储的负担。另一方面也可以看一下 FDB 的使用有没有替代。之前选型 FDB 产品也是因为性能上考虑,还有比如像 range 查询等,这些需要依赖 FDB 的优越的性能,我们也会得到一定的重构以及其他方案的支持。
本网站的文章部分内容可能来源于网络和网友发布,仅供大家学习与参考,如有侵权,请联系站长进行删除处理,不代表本网站立场,转载者并注明出处:https://jmbhsh.com/yulebagua/36406.html