一张从 MySQL 同步来的订单表 orders,在传统数仓里通常是一张 Hive 表:每天凌晨把业务库整张导一遍,写进当天的日期分区;报表第二天才看得到;要做即席查询,还得再导一份进 ClickHouse 或 Doris。订单 1001 下单一周后退了款,Hive 里那一行改不了,只能等下一次全量导入把它覆盖掉。

把这张表换成湖表(Iceberg、Paimon、Delta、Hudi 这类表格式管理的表)以后,这几件事都变了。这篇回答三个问题:数据湖是什么,它解决了 Hive 表的什么问题,又带来了什么。

数据湖是什么

数据湖就是放在对象存储上、用开放的表格式组织、可以被多种引擎读写的表。拆开看是四层:S3、OSS 这样的对象存储负责便宜地存;Parquet 这样的文件格式负责一个文件里怎么存;表格式负责「这张表由哪些文件组成」;Spark、Flink、Trino、Doris 这些引擎负责读写。

表格式夹在计算引擎和文件之间,用一层元数据描述一张表由哪些文件组成

Hive 表其实也是「存储 + 文件 + 元数据」,差别在中间那层。Hive 用目录来定义表:一个分区就是一个目录,目录里有什么文件,表里就有什么数据。湖表另记一份清单(快照),写明「这张表现在由哪些文件组成」;写入方先把新文件写好,最后用一次原子操作把「当前快照」换过去,读者要么看到旧的,要么看到新的,不会看到写了一半的数据。

湖表的提交:文件先写好、最后换指针,换之前读者只看到快照 1,换之后看到快照 2

下面四节的变化,都从这一个设计里长出来:


Hive 表

湖表

表由什么定义

目录里有哪些文件

快照清单里列了哪些文件

改一行

重写整个分区

按行更新,只写变化

新数据多久能查到

一批一批写分区,通常 T+1

每次提交一个快照,分钟级

查询怎么跳过数据

只能按分区跳过

清单里有每个文件的统计信息,按文件、行组跳过

谁能读

主要是 Hive 生态

任何实现了这个开放格式的引擎

变化一:支持更新,不用全量重算

Hive 表改不了一行,只能整块重写,所以每天全量重算;湖表能按行更新,每天只处理变了的那部分。

回到订单 1001。在 Hive 里,退款这件事没法只改那一行:要么等每天的全量导入,把整张 orders 重新导一遍;要么用 INSERT OVERWRITE 把它所在的分区整个重写。哪怕一天只有几千笔订单状态变了,也要搬动几亿行。Hive 3 其实有事务表(ACID),能做行级更新,但它绑定 ORC 格式和 Hive 自己的锁与合并机制(Hive Transactions),别的引擎用起来很别扭,大多数团队还是选择了整块覆盖。

湖表把「改一行」变成了普通操作。Spark 上一条 MERGE INTO 就能按主键把变更合进 Iceberg 表(Iceberg Spark Writes);用 Flink CDC 读 MySQL 的 Binlog,每一次插入、更新、删除都按主键写进湖表,湖里一直是一份最新的副本。底层只有两种做法:

改一行订单:写时复制重写它所在的文件,读时合并只记一笔「作废旧行、追加新行」,留给读的时候合并

方式

写入时

读取时

适合

写时复制

只重写受影响的文件(不是整个分区)

直接读,最快

读多写少、批量修数

读时合并

只追加变更:删除标记、新行

读时合并,要定期 compaction

写多、按主键频繁更新

四种表格式都支持更新,只是变更记成的样子不同:Hudi 分 Copy On Write 和 Merge On Read 两种表(Hudi);Iceberg v2 用删除文件,v3 改用删除向量,一张位图标出作废的行(Iceberg 规范);Delta 开启删除向量后同样只挂一张作废位图(Delta);Paimon 的主键表每个桶是一棵 LSM 树,新版本先落成小文件,读时按主键归并,后台再合并(Understand Files)。

真正省下来的是下游。表能更新以后,下游不用再拿全量重算:只要知道「这次提交里哪些行变了」,就只重算受影响的那部分。湖表能把变化本身交出来,增量读两次快照之间的差异,或者像 Paimon 那样直接流式消费变更日志;下游要做撤回式的聚合,还需要旧值,原因见Flink SQL 回撤机制与问题。

同样一笔退款:Hive 第二天把整张表重导一遍,下游跟着全量重算;湖表只合进这一行,下游只算变了的部分

变化二:时效性从 T+1 到分钟级

Hive 表靠一批一批写分区,数据通常晚一天;湖表每次提交就是一个快照,流式作业几分钟提交一次,数据几分钟后就能查到。

Hive 表也能写得更勤,比如每小时一个分区,但写得越勤,小文件越多,查询要列的目录和打开的文件越多,元数据服务压力越大,所以大多数链路停在 T+1。湖表的提交只是换一次快照指针,写入方可以很频繁地提交:Flink 往 Paimon 写,每次 checkpoint 提交一个快照,checkpoint 间隔设成一两分钟,数据就是分钟级可见(Understand Files)。订单 1001 退款几分钟后,湖表里那一行就是「已退款」,看板和下游作业都能读到。

同一笔退款,什么时候能查到:Hive 表等第二天凌晨的批,湖表等下一次 checkpoint

分钟级不是白来的。提交越频繁,小文件越多,要靠后台 compaction 把它们合并;Paimon 的 LSM 树和读时合并就是为这种高频更新设计的,代价是 compaction 一直占着资源,桶数也要在建表时想好。分钟级再往下走到秒级,表格式就不合适了,那是流存储的事,例如 Fluss。

变化三:湖仓一体,查询加速

以前 Hive 表查得慢,要再导一份进 OLAP 引擎;湖表的元数据里带着统计信息,OLAP 引擎直接查湖表,数据只留一份。

Hive 表查询只能按分区跳过数据:查某个城市的订单,也得把那几天的分区整个扫一遍。所以过去的常见做法是再导一份进 ClickHouse、Doris 这样的 OLAP 引擎:数据存两份,两边口径要对齐,导入作业也要有人维护。

湖表的清单里记着每个数据文件的分区、行数、每一列的最小值和最大值,查询可以先读清单,按文件跳过,再按 Parquet 行组跳过。解构数据引擎一文里,一次查询只从 60 MB 的数据里读了 0.9 MB。在这个基础上,OLAP 引擎不用再导一份:Doris 通过 Catalog 直接读 Hive、Iceberg、Hudi、Paimon 表,热数据缓存在本地,常用聚合用物化视图按分区增量刷新(Doris 湖仓概览、Data Cache)。这就是「湖仓一体」:湖负责存一份开放的数据,仓库的查询能力搬到湖上来用。

湖表不必搬进 Doris:元数据从 Catalog 拿,热数据缓存在本地盘,常用聚合用物化视图提前算好

加速也有代价:数据在对象存储里,冷数据每次要跨网络读,比不上数仓的本地盘;缓存和物化视图本身也要机器和运维。湖仓一体省掉的是第二份数据和导入链路,不是省掉了所有成本。

变化四:统一数据的趋势

表格式正在变成数据的公共底座:批处理、流处理、BI 和 AI 读写同一份开放格式的表,引擎可以换,数据不用搬。

过去一份数据往往有好几份拷贝:离线一份在 Hive,实时一份在 Kafka 和 OLAP 引擎里,给模型训练的再导一份。湖表让它们有机会合成一份:实时作业流式写、流式读,离线作业在同一张表上批量修数(我理解的流批一体讲了口径不一致为什么是流批分离最大的麻烦),BI 和即席查询直接读,训练任务也读同一个快照。

一份开放格式的湖表,多种引擎各取所需:数据不再为每个用途复制一份

这个趋势体现在三件事上:

  • 格式走向开放标准:Iceberg 已经是事实上的开放标准,还定义了 REST Catalog 的接口规范(REST Catalog Spec);Apache Polaris 这类目录服务按这套 API 实现,让 Spark、Flink、Trino、Doris 共用同一份元数据(Polaris)。

  • 格式之间互相靠拢:Paimon 可以额外生成 Iceberg 兼容的元数据,只认 Iceberg 的引擎也能读;它现在的定位是「把批、流和多模态 AI 放进同一种湖格式」(Paimon 文档)。

  • 数据种类变多:图片、音视频、向量这些多模态数据也开始用湖的方式管理,Lance 这类格式为随机读、加列和版本而设计,见Lance:为随机读、加列和版本而设计。

我的判断是:以后的数据平台会越来越像「一份开放格式的数据、一个统一的 Catalog、多种按需选用的引擎」。选表格式,本质上是在选这份数据以后能被谁读。

几种表格式怎么选


出身

更新的主要做法

侧重

Iceberg

Netflix

删除文件;v3 起删除向量

引擎中立、规范清晰,事实上的开放标准

Paimon

Flink 社区(原 Flink Table Store)

每个桶一棵 LSM 树

分钟级的流式更新,可以被流式读

Delta Lake

Databricks

写时复制;开删除向量后读时合并

与 Spark、Databricks 结合最紧

Hudi

Uber

COW 和 MOR 两种表类型

最早关注增量更新和增量消费

以追加为主、要被很多种引擎读,选 Iceberg;按主键高频更新、要分钟级可见,选 Paimon;整套平台在 Databricks 上,用 Delta。Paimon 的合并引擎还能做部分列更新、聚合和去重(Merge Engine),例如订单流和支付流各写同一个订单的一部分列,由湖表在合并时拼起来,替掉 Flink 双流 Join 的大状态。

代价:什么时候值得从 Hive 迁过来

技术上的好处很清楚,但迁不迁,看的是账算不算得过来:

  • 现有的 Hive 数仓已经稳定运行多年,迁移本身有成本和风险,迁完的第一天,报表的数字最好一个都不变。

  • 对象存储便宜,但查询要跨网络,缓存和物化视图又要额外的资源。

  • 小文件合并、快照清理、compaction 调优这些活,原来由数仓平台包办,现在落到团队自己身上。

所以我的判断是:数据湖更容易在新场景里落地,而不是靠替换旧数仓证明自己。最划算的是三类:需要频繁更新的 CDC 同步、实时和离线需要统一口径的链路、需要保留历史版本以便回溯的数据。那些一天跑一次、从不更新的 Hive 表,留在原地往往更省钱。

小结

数据湖是什么:放在对象存储上、用开放表格式组织、能被多种引擎读写的表;关键是表格式在文件之上记了一份快照清单。它解决了什么:Hive 表改不了一行、只能整块重写,写不勤、数据晚一天,查得慢、只好再导一份。它带来了什么:按行更新,下游只算变化;流式提交,数据分钟级可见;元数据带统计,OLAP 引擎直接查湖;格式开放,批、流、AI 共用一份数据。

想看快照和清单在一次提交、一次查询里具体长什么样,接着读解构数据引擎;想要秒级以下的流式更新,读Fluss:重塑实时开发。

参考资料


全文评论

0

正在加载评论…