一张从 MySQL 同步来的订单表 orders,在传统数仓里通常是一张 Hive 表:每天凌晨把业务库整张导一遍,写进当天的日期分区;报表第二天才看得到;要做即席查询,还得再导一份进 ClickHouse 或 Doris。订单 1001 下单一周后退了款,Hive 里那一行改不了,只能等下一次全量导入把它覆盖掉。
把这张表换成湖表(Iceberg、Paimon、Delta、Hudi 这类表格式管理的表)以后,这几件事都变了。这篇回答三个问题:数据湖是什么,它解决了 Hive 表的什么问题,又带来了什么。
数据湖是什么
数据湖就是放在对象存储上、用开放的表格式组织、可以被多种引擎读写的表。拆开看是四层:S3、OSS 这样的对象存储负责便宜地存;Parquet 这样的文件格式负责一个文件里怎么存;表格式负责「这张表由哪些文件组成」;Spark、Flink、Trino、Doris 这些引擎负责读写。
Hive 表其实也是「存储 + 文件 + 元数据」,差别在中间那层。Hive 用目录来定义表:一个分区就是一个目录,目录里有什么文件,表里就有什么数据。湖表另记一份清单(快照),写明「这张表现在由哪些文件组成」;写入方先把新文件写好,最后用一次原子操作把「当前快照」换过去,读者要么看到旧的,要么看到新的,不会看到写了一半的数据。
下面四节的变化,都从这一个设计里长出来:
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 回撤机制与问题。
变化二:时效性从 T+1 到分钟级
Hive 表靠一批一批写分区,数据通常晚一天;湖表每次提交就是一个快照,流式作业几分钟提交一次,数据几分钟后就能查到。
Hive 表也能写得更勤,比如每小时一个分区,但写得越勤,小文件越多,查询要列的目录和打开的文件越多,元数据服务压力越大,所以大多数链路停在 T+1。湖表的提交只是换一次快照指针,写入方可以很频繁地提交:Flink 往 Paimon 写,每次 checkpoint 提交一个快照,checkpoint 间隔设成一两分钟,数据就是分钟级可见(Understand Files)。订单 1001 退款几分钟后,湖表里那一行就是「已退款」,看板和下游作业都能读到。
分钟级不是白来的。提交越频繁,小文件越多,要靠后台 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)。这就是「湖仓一体」:湖负责存一份开放的数据,仓库的查询能力搬到湖上来用。
加速也有代价:数据在对象存储里,冷数据每次要跨网络读,比不上数仓的本地盘;缓存和物化视图本身也要机器和运维。湖仓一体省掉的是第二份数据和导入链路,不是省掉了所有成本。
变化四:统一数据的趋势
表格式正在变成数据的公共底座:批处理、流处理、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:重塑实时开发。
参考资料
Michael Armbrust 等,Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics,CIDR 2021。
Michael Armbrust 等,Delta Lake: High-Performance ACID Table Storage over Cloud Object Stores,VLDB 2020。
Apache Iceberg 表格式规范、REST Catalog Spec、Spark Writes(MERGE INTO)
全文评论
0正在加载评论…