十分钟读懂字节跳动的 Doris 湖仓分析实践

数据中台大数据数据湖仓
1. Doris 简介

Doris 是一种 MPP 架构的分析型数据库,主要面向多维分析,数据报表,用户画像分析等场景。自带分析引擎和存储引擎,支持向量化执行引擎,不依赖其他组件,兼容 MySQL 协议。Apache Doris 具备以下几个特点:

  • 良好的架构设计, 支持高并发低延时的查询服务,支持高吞吐量的交互式分析。多 FE 均可对外提供服务,并发增加时,线性扩充 FE 和 BE 即可支持高并发的查询请求。
  • 支持批量数据 load 和流式数据 load, 支持数据更新。支持 Update/Delete 语法,unique/aggregate 数据模型,支持动态更新数据,实时更新聚合指标。
  • 提供了高可用, 容错处理,高扩展的企业级特性。FE Leader 错误异常,FE Follower 秒级切换为新 Leader 继续对外提供服务。
  • 支持聚合表和物化视图。 多种数据模型,支持 aggregate,replace 等多种数据模型,支持创建 rollup 表,支持创建物化视图。rollup 表和物化视图支持动态更新,无需用户手动处理。
  • MySQL 协议兼容, 支持直接使用 MySQL 客户端连接,非常易用的数据应用对接。

Doris 由 Frontend(以下简称 FE)和 Backend(以下简称 BE)组成,其中 FE 负责接受用户请求,编译,优化,分发执行计划,元数据管理,BE 节点的管理等功能,BE 负责执行由 FE 下发的执行计划,存储和管理用户数据。

picture.image

2. 数据湖格式 Hudi 简介

Hudi 是下一代流式数据湖平台,为数据湖提供了表格式管理的能力,提供事务,ACID,MVCC,数据更新删除,增量数据读取等功能。支持 Spark,Flink,Presto,Trino 等多种计算引擎。

picture.image

Hudi 根据数据更新时行为不同分为两种表类型:

picture.image

针对 Hudi 的两种表格式,存在3种不同的查询类型:

picture.image

3. Doris 分析 Hudi 数据的技术背景

在数仓业务中,随着业务对数据实时性的要求越来越高,T+1数仓业务逐渐往小时级,分钟级,甚至秒级演进。实时数仓的应用也越来越广,也经历了多个发展阶段。目前存在着多种解决方案。

3.1 Lambda 架构

Lambda 将数据处理流分为在线分析和离线分析分为两条不同的处理路径,两条路径互相独立,互不影响。

离线分析处理T+1数据,使用 Hive/Spark 处理大数据量,不可变数据,数据一般存储在 HDFS 等系统上。如果遇到数据更新,需要 overwrite 整张表或整个分区,成本比较高。

在线分析处理实时数据,使用 Flink/Spark Streaming 处理流式数据,分析处理秒级或分钟级流式数据,数据保存在 Kafka 或定期(分钟级)保存到 HDFS 中。

该套方案存在以下缺点:

  • 同一套指标可能需要开发两份代码来进行在线分析和离线分析,维护复杂
  • 数据应用查询指标时可能需要同时查询离线数据和在线数据,开发复杂
  • 同时部署批处理和流式计算两套引擎,运维复杂
  • 数据更新需要 overwrite 整张表或分区,成本高

3.2 Kappa 架构

随着在线分析业务越来越多,Lambda 架构的弊端就越来越明显,增加一个指标需要在线离线分别开发,维护困难,离线指标可能和在线指标对不齐,部署复杂,组件繁多。于是 Kappa 架构应运而生。

Kappa 架构使用一套架构处理在线数据和离线数据,使用同一套引擎同时处理在线和离线数据,数据存储在消息队列上。

Kappa 架构也有一定的局限:

  • 流式计算引擎批处理能力较弱,处理大数据量性能较弱
  • 数据存储使用消息队列,消息队列对数据存储有有效性限制,历史数据无法回溯
  • 数据时序可能乱序,可能对部分对时序要求比较严格的应用造成数据错误
  • 数据应用需要从消息队列中取数,需要开发适配接口,开发复杂

3.3 基于数据湖的实时数仓

针对 Lambda 架构和 Kappa 架构的缺陷,业界基于数据湖开发了 Iceberg, Hudi, DeltaLake 这些数据湖技术,使得数仓支持 ACID, Update/Delete, 数据 Time Travel, Schema Evolution 等特性,使得数仓的时效性从小时级提升到分钟级,数据更新也支持部分更新,大大提高了数据更新的性能。兼具流式计算的实时性和批计算的吞吐量,支持的是近实时的场景。

以上方案中其中基于数据湖的应用最广,但数据湖模式无法支撑更高的秒级实时性,也无法直接对外提供数据服务,需要搭建其他的数据服务组件,系统较为复杂。基于此背景下,部分业务开始使用 Doris 来承接,业务数据分析师需要对Doris与Hudi中的数据进行联邦分析,此外在 Doris 对外提供数据服务时既要能查询Doris中数据,也要能加速查询离线业务中的数据湖数据,因此我们开发了Doris访问数据湖Hudi中数据的特性。

4. Doris 分析 Hudi 数据的设计原理

基于以上背景,我们设计了 Apache Doris 中查询数据湖格式 Hudi 数据,因 Hudi 生态为 java 语言,而 Apache Doris 的执行节点 BE 为 C++ 环境,而 C++ 无法直接调用 Hudi java SDK,针对这一点,我们有四种解决方案:

(1)实现 Hudi C++ client,在 BE 中直接调用 Hudi C++ client 去读写 Hudi 表。

该方案需要完整实现一套 Hudi C++ client,开发周期较长,后期 Hudi 行为变更需要同步修改 Hudi C++ client,维护较为困难。

(2)BE 通过 thrift 协议发送读写请求至 Broker,由 Broker 调用 Hudi java client 读取 Hudi 表。

该方案需要在 Broker 中增加读写 Hudi 数据的功能,目前 Broker 定位仅为 fs 的操作接口,引入 Hudi 打破了 Broke 的定位。第二,数据需要在 BE 和 Broker 之间传输,性能较低。

(3)在 BE 中使用 JNI 创建 JVM ,加载 Hudi java client 去读写 Hudi 表。

该方案需要在BE进程中维护JVM,有JVM调用Hudi java client对Hudi进行读写。读写逻辑使用Hudi社区java实现,可以维护与社区同步;同时数据在同一个进程中进行处理,性能较高。但需要在BE维护一个JVM,管理较为复杂。

(4)使用 BE arrow parquet c++ api 读取 hudi parquet base file,hudi表中的 delta file 暂不处理。

该方案可以由BE直接读取hudi表的parquet文件,性能最高。但当前不支持base file和delta file的合并读取,因此仅支持COW表Snapshot Queries和MOR表的Read Optimized Queries,不支持Incremental Queries。

综上,我们选择方案四,第一期实现了COW表Snapshot Queries和MOR表的Read Optimized Queries,后面联合Hudi社区开发base file和delta file合并读取的C++接口。

5. Doris 分析 Hudi 数据的技术实现

Doris中查询分析Hudi外表使用步骤非常简单。

5.1 创建 Hudi 外表

建表时指定engine为Hudi,同时指定Hudi外表的相关信息,如hive metastore uri,在hive metastore中的database和table名字等。

建表仅仅在Doris的元数据中增加一张表,无任何数据移动。

建表时支持指定全部或部分hudi schema,也支持不指定schema创建hudi外表。指定schema时必须与hiveMetaStore中hudi表的列名,类型一致。

Example:

   CREATE TABLE example_db.t_hudi    
    ENGINE=HUDI    
    PROPERTIES (   
    "hudi.database" = "hudi_db",    
    "hudi.table" = "hudi_table",    
    "hudi.hive.metastore.uris"  =  "thrift://127.0.0.1:9083"   
   );

    CREATE TABLE example_db.t_hudi (    
     column1 int,    
     column2 string)    
     ENGINE=HUDI   
     PROPERTIES (    
     "hudi.database" = "hudi_db",   
     "hudi.table" = "hudi_table",   
     "hudi.hive.metastore.uris"  =  "thrift://127.0.0.1:9083" 
    );

5.2 查询 Hudi 外表

  • 查询Hudi数据表时,FE在analazy阶段会查询元数据获取到Hudi外表的的hive metastore地址,从Hive metastore中获取hudi表的schema信息与文件路径。
    • 获取hudi表的数据地址
    • FE规划fragment增加HudiScanNode。HudiScanNode中获取Hudi table对应的data file文件列表。
    • 根据Hudi table获取的data file列表生成scanRange
    • 下发HudiScan 任务至BE节点
    • BE节点根据HudiScanNode指定的Hudi外表文件路径调用native parquet reader进行数据读取。

picture.image

6. 后期规划

目前 Apche Doris 查询 Hudi 表已合入社区,当前已支持 COW 表的 Snapshot Query,支持 MOR 表的 Read Optimized Query。对 MOR 表的 Snapshot Query 暂时还未支持,流式场景中的 Incremental Query 也没有支持。

后续还有几项工作需要处理,我们和社区也在积极合作进行中:

(1)MOR 表的 Snapshot Query。MOR 表实时读需要合并读取 Data file 与对应的 Delta file,BE 需要支持 Delta file AVRO 格式的读取,需要增加 avro 的 native 读取方式。

(2)COW/MOR 表的 Incremental Query。支持实时业务中的增量读取。 (3)BE 读取 Hudi base file 和 delta file 的 native 接口。目前 BE 读取 Hudi 数据时,仅能读取 data file,使用的是 parquet 的 C++ SDK。后期我们和联合 Hudi 社区提供Huid base file 和d elta file 的 C++/Rust 等语言的读取接口,在 Doris BE 中直接使用 native 接口来查询Hudi数据。

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论