更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群
前言
当涉及到企业分析场景时,所使用的数据通常源自多样的业务数据,这些数据系统大多采用以行为主的存储结构,比如支付交易记录、用户购买行为、传感器报警等。在数仓及分析领域,海量数据则主要采按列的方式储存。因此,将数据从行级转换成列级存储是建立企业数仓的基础能力。
传统方式是采用 Extract-Transform-Load (ETL)来将业务数据转换为适合数仓的数据模型,然而,这依赖于独立于数仓外的 ETL 系统,因而维护成本较高。但随着云计算时代的到来,云数据仓库具备更强扩展性和计算能力,也要求改变传统的 ELT 流程。
火山引擎 ByteHouse 是一款基于开源 ClickHouse 推出的云原生数据仓库,为用户提供极速分析体验,能够支撑实时数据分析和海量数据离线分析,同时还具备便捷的弹性扩缩容能力,极致分析性能和丰富的企业级特性。凭借其强大的计算能力,可以全面支持 Extract-Load-Transform (ELT)的能力,从而使用户免于维护多套异构系统。
具体而言,用户可以将数据导入后,通过自定义的 SQL 语句,在 ByteHouse 内部进行数据转换,而无需依赖独立的 ETL 系统及资源。这样,用户只需要采用统一的 SQL 方式来完成数据转换操作。
在本文中,我们将重点介绍 ByteHouse 遇到的挑战,以及如何通过 3 大能力建设实现完备的 ELT 能力。
痛点以及挑战
我们先从一个简单的 SSB(start-schema-benchmark)场景出发, 其中包含:
-
1 个事实表: lineorder
-
4 个维度表:customer, part, supplier, dwdate
在 SSB 的查询分析中,我们发现大部分的查询都涉及到事实表和维表的 join,因此可以通过 Transform 的步骤,将事实表“打平”。 打平所用到的 SQL 如下:
insert into ssb_flat
select * from
lineorder l
join customer c on l.lo_custkey = c.c_custkey
join part p on l.lo_partkey = p.p_partkey
join supplier s on l.lo_suppkey = s.s_suppkey
where l.lo_orderdate = <bizdate>
之后的查询分析可以通过对ssb_flat
的单表扫描来规避很多join
操作,其性能能有显著提升。 这个“打平”的过程,就是“Transform”的一种。 实际生产场景中的“Transform”的 case 会更多也更复杂。 但是通过以上这个“打平”的过程,我们可以分析出这类操作在数据库上的普遍性痛点。
变换操作跟普通查询相比,有几个大的区别:
-
变换操作执行时间久, 整体重试成本高
-
变换操作没有返回值,我们只关心他成功或者失败
-
变化操作读写量大,占用资源
具体来说:
-
首先对于 ByteHouse 来讲,其擅长的临时查询时间都在秒级,查询中间出故障一般都直接返回错误,交由上游重试。而在 ETL 场景下,一个任务如果执行了 50 分钟,由于某些原因故障了,重试相当于前 50 分钟的资源都被浪费了,显然不能被接受。
-
其次,由于 ETL 没有返回结果,客户端需要保持一个 idle 的长链接,很有可能由于配置原因超时,同时大量的并发任务也会吃掉正常的链接资源。
-
最后,由于 ETL 任务读写量大,多个任务并发的时候,需要考虑到资源的分配,以达到性能和隔离的平衡。
针对这三个痛点,ByteHouse 针对性的设计了三个功能,即长任务管理、异步提交和查询队列。
功能一:长任务管理
通常情况下,我们可以用 settings max_execution_time 来控制一个查询的超时时间,ByteHouse 提供了事务支持来保障读写操作的原子性。
但是并这不足以覆盖 ETL 任务的需求。 在长时间的任务执行中,更容易遇到系统性故障,如节点 OOM 等。在这种情况下,由客户端重试并不是个优雅的方案。
在 ByteHouse 中,一个 SQL 查询会被转化为一系列的算子。 我们希望提升算子的容错能力以更好的应对长时间查询下的系统故障。目前的版本中,ByteHouse 已经针对聚合,排序,关联等算子提供了 disk spill 功能。 具体来说,当某个算子无法获得足够的内存时,我们允许这个算子将一部分数据缓存在磁盘上,以此在资源紧张的情况下仍能够完成工作。
例如在排序算子中,我们引入了 external merge sort 的能力,并通过max_bytes_before_external_sort
来控制外部排序能力。在下图左边是未开启 spill 的排序查询计划,右边是开启 spill 的计划。
可以看到在开启 external sort 之后,ByteHouse 引入了 BufferingToFileTransform,MergingSortedTransform 两个算子。同样的,ByteHouse 里的聚合,关联算子都做了类似的优化例如 grace hash join 等。
接下来 ByteHouse 也打算针对 exchange 操作,进一步提升 shuffle 操作的容错性。
功能二:异步提交能力
面对大量长耗时的 ETL 任务时,传统的同步执行的方式需要客户端等待服务端返回。 这样很容易出现客户端超时,进而影响后续任务执行的问题。
同时,在这种场景中,用户并不关心单个任务或请求的相应时间,只期望任务能在特定时间内完成,并对可靠性等要求较高。 因此 ByteHouse 提供了异步提交的任务的能力。
ByteHouse 用户现在可以通过 setting enable_async_execution
来提交一个异步任务。ByteHouse 在收到这类任务之后,会返回一个异步任务 ID, 例如 ff46fccf-d872-4c68-bdb2-c8c18fc178f5
。 之后客户端可以选择间歇性轮训来获得任务的最终状态。
ByteHouse 提供了 show async status 'ff46fccf-d872-4c68-bdb2-c8c18fc178f5'
的指令来获得状态。 同时 ByteHouse 也提供了 kill query 'ff46fccf-d872-4c68-bdb2-c8c18fc178f5'
的指令来取消某些异步的查询。
功能三:查询队列
离线加工面对大量请求时,当系统超载,需要一定的排队机制使 query 请求挂起,等待集群释放资源后再进行调度。ByteHouse 为此提供了查询队列能力。
ByteHouse 可以允许用户从三个维度度来定义一个队列,即: 队列大小,总 CPU 占用率,和总内存占用率。
在 ByteHouse 中,Resource Manager 组件可以用来监听各个队列中的查询指标,得到队列的资源使用率。 当用户向一个队列提交查询时,如果队列还未达到上限,ByteHouse 会将这个查询入队,否则拒绝掉这个查询。
此后,ByteHouse 会时刻检查队列的资源利用率,当空闲资源高过某个阀值时,Bytehouse 会将等待中的查询出队。当某个处于等待期的查询被取消时,ByteHouse 也会将其移出队列。利用查询队列,用户在编排 ETL 任务时不用担心底层资源过载,因此可以更加自由。
之后 ByteHouse 也在计划增加优先级队列功能。届时,用户可以为 ETL 任务和即时查询创建不同队列优先级,这样 ELT 任务和即时查询可以跑在同一个计算组中而不会显著的相互影响。
总结
以上介绍了 ByteHouse 在支持 ETL 能力中的一些技术细节。其中长任务,异步提交已经队列功能已经在 preview 版本中上线。接下来,ByteHouse 也会继续扩展 ETL 能力,包括支持更多的 ETL 相关的转换函数、长任务容错、优先级队列等。
除了 ELT 能力之外,火山引擎 ByteHouse 基于独家自研的高可用引擎及查询优化器,可以为企业提供快速、稳定、安全的查询服务和数据写入性能。在云原生架构下,火山引擎 ByteHouse 提供了极致扩展的统一数据分析平台,具有出色的弹性伸缩和可扩展性,确保资源可以灵活地水平扩展;
同时,ByteHouse 支持多级资源隔离,为用户资源提供更安心的安全保障。火山引擎 ByteHouse 还从业务角度出发提供了完整的运维监控和排障能力,帮助企业实现业务云上托管,降低运维成本。欢迎登陆火山引擎 ByteHouse 官网体验。