大数据数仓存储与查询链路技术总结
大数据数仓存储与查询链路技术总结文档
本文梳理Hive元数据、SparkSQL、Trino、Doris、Parquet等常用组件的调用链路,区分不同业务场景下的架构差异,用于帮助对大数据技术不熟悉的小伙伴理解组件分层。
文档约定:
- 分层固定为三层:计算引擎层、元数据层、存储层;
- 每个场景独立流程图,只保留场景内用到组件,剔除无关内容,避免图面复杂;
- 本文档暂不深入湖仓一体的大量优化细节,重点区分【裸文件Hive表】与【Iceberg表】的本质差异。
1 名词解释
| 名词 | 释义 |
|---|---|
| 计算引擎 | 负责解析SQL、生成执行计划、执行过滤/聚合/关联计算。本文档涉及Trino、SparkSQL、HiveSQL、Apache Doris。 Trino:交互式查询引擎,本身不持久化数据,依赖外部元数据与存储; SparkSQL:支持批处理ETL+查询,可读写对象存储/HDFS; HiveSQL:传统Hive的SQL引擎; Doris:存储计算一体化OLAP引擎,支持内部表,也可通过外表读取外部存储上的文件。 |
| 元数据层 | 记录表结构、字段类型、分区信息、文件路径、分片信息。 Hive Metastore(HMS):最通用元数据服务,Hive/Spark/Trino/Doris外表共用; Doris内置元数据:Doris内部表自用,不依赖HMS。 |
| 存储层 | 存放物理数据载体。包含底层存储介质(HDFS、COS、S3、BE本地磁盘)+ 文件格式。 |
| 文件格式 | 单个数据文件内部的序列化组织方式,独立于上层表定义。 Parquet:列式存储,支持列裁剪、RowGroup级谓词下推; ORC:Hive原生列式格式,Hive事务支持更好。 |
| 分区表 | Hive/Spark层面的表逻辑组织方式,按指定字段拆分为不同目录,查询时做分区剪枝,跳过无关目录。分区属于元数据定义,不修改Parquet/ORC文件内部结构。 |
| Iceberg | 表格式(不是文件格式),底层仍然使用Parquet。在裸Parquet之上增加独立快照、manifest、元数据文件,提供ACID写入、时间旅行、并发写入、schema演进能力。HMS仅记录Iceberg表的根路径,快照、文件清单由Iceberg自行管理。 |
| 裸Parquet表 | 普通Hive外部表,直接在HDFS/对象存储目录写入parquet文件,HMS只记录表和分区目录,没有额外的快照元数据。写入无事务保障。 |
| StorageHandler | Hive体系内的适配接口,用于HiveSQL读写不同文件格式;Spark复用该接口,Trino读取Hive Catalog时不使用StorageHandler。本文档流程图不单独画出,封装在HiveSQL引擎内部。 |
2 整体架构说明
2.1 三层架构综合流程图(总览)
仅用于理解三层边界,实际业务中一般不会同时全部组件一起使用。
2.2 通用查询时序(所有HMS托管外部表通用流程)
场景:Trino/SparkSQL查询HMS托管的外部Parquet表
2.3 存储和格式比较
Parquet / ORC / Avro / TextFile 对比表 > 说明:基于Hive/SparkSQL/Trino生态,从存储类型、编码特点、适用场景、优缺点整理,数仓运维视角。
| 特性 | TextFile | Avro | Parquet | ORC |
|---|---|---|---|---|
| 存储类型 | 行式存储 | 行式存储 | 列式存储 | 列式存储 |
| 数据组织 | 纯文本,一行一条记录 | 二进制行存储,一条记录完整存放 | 按列拆分存储,同一列数据连续存放 | 列式存储,Hive原生优化列式 |
| 序列化方式 | 文本(UTF-8) | Avro二进制序列化 | Parquet二进制 | ORC二进制 |
| Schema支持 | 无内置schema,靠建表语句定义 | 自带schema,支持schema演进(增删字段、兼容旧数据) | 自带schema,支持schema演进 | 自带schema,支持schema演进 |
| 压缩 | 可开启Gzip/Snappy;压缩效率差 | 压缩效果较好 | 压缩率高,同数据体积更小 | 压缩率高,Hive生态下压缩优化强 |
| 读取模式 | 读取必须加载整行;读少量字段也要读完整行 | 读取整行记录,无法只读取部分列 | 支持列裁剪,只读取查询用到的列;支持RowGroup级谓词下推 | 支持列裁剪,支持Stripe/Index谓词下推,内置轻量索引 |
| 写入性能 | 写入快,无需序列化 | 写入性能较好,CDC场景友好 | 批量写入性能不错,小批量写入容易产生小文件 | Hive引擎写入性能最优 |
| Hive事务支持 | 不支持 | 不支持 | Hive事务支持一般 | 原生支持Hive ACID事务(Update/Delete) |
| 跨引擎兼容性 | 极好,所有引擎都可读 | 很好,Spark/Hive/Trino通用 | 跨引擎兼容性最好,Spark默认格式,Hive/Trino/Doris外表都支持 | Hive生态最优;跨引擎支持弱于Parquet |
| 典型适用场景 | 原始日志临时落地、调试查看内容;临时小表 | CDC数据接入、数据交换、需要频繁schema变更场景 | 通用离线数仓、湖仓(Iceberg/Hudi底层默认),多引擎共享数据 | 传统Hive数仓,需要Hive事务、Hive批量ETL场景 |
| 主要缺点 | 体积大,查询慢;无法谓词下推;容易脏数据 | 行式,查询大表只取少数列时IO开销大 | Hive原生事务能力弱;元数据Footage略大 | Trino/Spark读取性能略弱于Parquet,跨引擎通用性稍差 |
补充运维备注
- TextFile:可直接用
cat查看文件内容,调试方便,但生产大表严禁使用,IO开销巨大。 - Avro虽然是行式,但schema内嵌在文件中,适合数据流转,多用于数据同步、CDC采集落地,不适合海量离线分析查询。
- Parquet:多引擎共用数仓首选,如果你的数据要同时给Spark、Trino、Doris外表查询,优先Parquet。Iceberg/Hudi底层默认采用Parquet。
- ORC:Hive专属优化,老Hive数仓、需要Hive内部做update/delete的场景优先选ORC;如果Trino为主查询引擎,优先Parquet。
3 分场景架构图+场景说明
规则:每个场景独立流程图,三层固定:计算引擎|元数据|存储;只保留当前场景用到组件。
场景1:Trino查询,HMS管理元数据,HDFS存储Parquet
业务场景:离线数仓Hive外部分区表,Trino做交互式查询,数据文件存HDFS,文件格式Parquet。 适用场景:分析师即时查询、报表查询,数据主要为离线批量写入。
表结构、分区、HDFS文件路径] end subgraph 存储 subgraph 文件格式 Parquet[Parquet] end HDFS[HDFS分布式存储] end Trino <--> HMS Trino --> Parquet Parquet --> HDFS
链路说明
- Trino连接HMS,获取表定义、分区信息;
- 根据where条件做分区剪枝,过滤不需要扫描的HDFS目录;
- Trino读取HDFS上Parquet文件,引擎内部解析parquet,做列裁剪、RowGroup过滤;
- 内存计算后返回结果。
场景2:SparkSQL查询/ETL,HMS管理元数据,COS存储Parquet(裸Parquet Hive表)
业务场景:云上数仓,Spark负责ETL写入+查询,HMS管理元数据,数据存腾讯云COS对象存储,普通Hive外部分区表(裸Parquet,无Iceberg)。 适用场景:每日一次性批量追加数据,无并发写、无更新、不需要回滚。
表结构、分区、COS文件路径] end subgraph 存储 subgraph 文件格式 Parquet[Parquet] end COS[COS对象存储] end SparkSQL <--> HMS SparkSQL --> Parquet Parquet --> COS
链路说明
- SparkSQL读写HMS,维护分区元数据;
- ETL任务生成Parquet文件写入COS;
- 查询时Spark读取COS上parquet文件,做分区剪枝和列式过滤。> > 风险点:裸Parquet表写入无事务,任务中途失败会产生残次文件;多任务同时写同一分区会产生脏数据,无法回滚。
场景3:Doris内部表查询,Doris自研元数据与本地存储
业务场景:Doris内部业务表,数据写入Doris BE节点本地磁盘,不依赖HMS、HDFS、对象存储、Parquet。 适用场景:高并发报表、实时指标、点查询,存储计算一体化。
表、分区、分片信息] end subgraph 存储 DorisDisk[Doris BE本地磁盘
Doris自研列式存储] end Doris <--> DorisMeta Doris --> DorisDisk
链路说明
- Doris自带计算、元数据管理、存储三层能力;
- 数据分片存BE本地磁盘,Doris内部完成分区、分片剪枝;
- 不依赖外部HMS,不使用Parquet。
场景4:Doris外表查询,HMS管理元数据,S3存储Parquet
业务场景:Doris通过Hive Catalog外表,直接查询S3上Hive外部Parquet表,Doris不存储原始数据。 适用场景:Doris直接分析湖侧原始数据,不需要把数据导入Doris内部。
表结构、分区、S3文件路径] end subgraph 存储 subgraph 文件格式 Parquet[Parquet] end S3[S3对象存储] end Doris <--> HMS Doris --> Parquet Parquet --> S3
链路说明
- Doris连接HMS获取表、分区信息;
- 分区剪枝后,Doris直接读取S3上Parquet文件;
- Doris仅做查询,不会修改S3上原始文件。
场景5:HiveSQL查询,HMS管理元数据,HDFS存储ORC
业务场景:传统Hive离线数仓,HiveSQL作为计算引擎,HDFS存储ORC格式。 适用场景:老Hive数仓,需要Hive原生事务(ACID V2)。
表结构、分区、HDFS文件路径] end subgraph 存储 subgraph 文件格式 ORC[ORC] end HDFS[HDFS分布式存储] end HiveSQL <--> HMS HiveSQL --> ORC ORC --> HDFS
链路说明
- HiveSQL访问HMS获取元数据;
- Hive内部加载StorageHandler,读取HDFS上ORC文件;
- ORC对Hive引擎优化更好,原生支持Hive事务。
场景6:SparkSQL读写Iceberg表,HMS管理元数据,S3底层Parquet
业务场景:湖仓场景,Spark读写Iceberg表,底层物理文件依旧是Parquet,HMS只记录Iceberg表根路径。 适用场景:需要并发写入、数据回滚、时间旅行、部分数据更新(MERGE INTO)。
Iceberg表根路径] end subgraph 存储 subgraph 文件格式 Parquet[Parquet] end S3[S3对象存储] end SparkSQL <--> HMS SparkSQL --> Parquet Parquet --> S3
链路说明 & 和场景2(裸Parquet)对比
底层物理文件都是Parquet,存储介质都是S3,HMS都参与。核心差别在元数据管理主体:
- 场景2(裸Parquet):HMS记录分区目录,没有快照。写入无事务,任务失败产生残次文件;无法回滚,不支持并发写同一分区。适合简单一次性追加。
- 场景6(Iceberg):HMS只存Iceberg表根路径;快照、manifest、文件清单全部由Iceberg自己维护。写入通过元数据指针原子切换,保证ACID;支持时间旅行、MERGE INTO更新、并发写入。> > 代价:增加Iceberg元数据维护成本,需要定时执行Compaction合并小文件、清理过期快照。
4 场景对比汇总表
| 序号 | 场景 | 计算引擎 | 元数据来源 | 存储介质 | 文件格式 | 适用场景 | 核心特点 |
|---|---|---|---|---|---|---|---|
| 1 | Trino读Hive表HDFS | Trino | HMS | HDFS | Parquet | 交互式报表查询 | Trino联邦查询,只读外部Hive数据 |
| 2 | SparkSQL读写裸Parquet Hive表,COS | SparkSQL | HMS | COS | Parquet | 每日一次性批量追加 | 简单稳定,无事务,不支持更新回滚 |
| 3 | Doris内部表 | Doris | Doris内置元数据 | BE本地磁盘 | Doris自研 | 高并发实时指标、报表 | 存储计算一体,不依赖外部元数据 |
| 4 | Doris外表读取S3 Parquet | Doris | HMS | S3 | Parquet | Doris直接查询湖数据 | 不导入数据,直接查询对象存储 |
| 5 | HiveSQL读取ORC HDFS | HiveSQL | HMS | HDFS | ORC | 传统Hive离线数仓 | ORC适配Hive原生事务能力 |
| 6 | SparkSQL读写Iceberg表 | SparkSQL | HMS | S3 | Parquet | 湖仓,并发写入、更新、回滚 | Parquet底层,Iceberg增加快照与事务能力 |
5 运维关注点小结
- 裸Hive分区表(场景1/2/5):运维重点关注分区生命周期、小文件治理、元数据同步;写入要避免多任务并发写同一分区。
- Iceberg表:除小文件外,额外关注快照过期清理、Compaction任务调度,防止元数据文件持续膨胀。
- Doris内部表:运维重点在BE节点磁盘、分片均衡、副本;Doris外表重点关注对象存储AK/SK权限。
- 跨引擎共用同一份HMS+Parquet文件:Trino、Spark、Hive可以读取同一份物理文件,需要保证字段类型、时区定义统一,避免解析不一致。