数据湖选型全景:存储、表格式、计算引擎怎么选

数据湖不是一个单一的产品,而是一套由多个组件组合而成的架构。选型时需要在四个层次分别做决策:存储层(数据放哪)、表格式层(数据怎么组织)、计算引擎层(数据怎么处理)、元数据管理层(数据怎么管理)。本文逐层拆解各主流选项的特点,并给出典型的组合方案。

存储层:数据放哪

存储层是数据湖的地基,决定了数据的物理存放位置和访问成本。

HDFS(自建 Hadoop 集群)

HDFS 是传统大数据架构的存储核心,数据存在自己的服务器上。

优点:数据完全自控,网络带宽不受云厂商限制,计算与存储在同一集群(数据本地性),适合计算密集型的大规模批处理。

缺点:运维成本高(需要专职团队维护 NameNode、DataNode),扩缩容不灵活(加机器周期长),存储与计算耦合(计算资源闲置时存储资源也在消耗成本)。

适合场景:已有大规模 Hadoop 集群、对数据安全有极高要求(不能上云)、网络带宽是瓶颈的场景(如超大规模 Shuffle 作业)。

云对象存储(S3 / OSS / GCS)

云对象存储是现代数据湖的主流选择。AWS S3、阿里云 OSS、腾讯云 COS、Google GCS 的设计思路相同:按存储量和请求次数计费,存储与计算完全解耦。

优点:存储成本极低(约 $0.02/GB/月,比 HDFS 低 3-5 倍);弹性无限,不需要提前规划容量;存算分离,计算集群可以按需启停;多个计算集群可以共享同一份数据。

缺点:网络延迟比 HDFS 高(跨网络访问 vs 本地磁盘);小文件问题更突出(每次 LIST 操作有延迟);数据出云有流量费用。

适合场景:新建数据湖首选,尤其是业务规模不稳定、需要弹性扩缩容的团队。国内上云首选阿里云 OSS(与 EMR/MaxCompute 生态集成最好)或腾讯云 COS。

选型建议

2026 年新建数据湖,默认选云对象存储。除非有合规要求(金融、政务数据不能出境)或已有大规模 HDFS 集群,否则 HDFS 的运维成本很难被收益覆盖。存算分离是大势所趋,云上的 EMR 集群可以在夜间关停,大幅降低计算成本。

表格式层:数据怎么组织

表格式层是数据湖近年来最重要的技术演进,解决了"文件堆"变成"可管理的表"的问题。三个主流选项:Delta Lake、Apache Iceberg、Apache Hudi。

Delta Lake

由 Databricks 开源(2019 年),是 Databricks 平台的核心存储格式。

核心机制:在 Parquet 文件之上维护一个 _delta_log 目录,记录每次写操作的 JSON 日志(事务日志)。读取时通过回放事务日志确定当前状态,支持快照隔离(Snapshot Isolation)。

核心特性

  • ACID 事务:并发写入不会互相覆盖,写失败自动回滚
  • 时间旅行(Time Travel):通过版本号或时间戳查询历史数据,默认保留 30 天
  • Schema Evolution:支持新增列、修改列类型,不需要重写历史数据
  • OPTIMIZE + Z-ORDER:自动合并小文件,并按指定列做多维聚类,提升查询性能
  • Change Data Feed(CDF):记录每行数据的变更(INSERT/UPDATE/DELETE),方便下游增量消费
from delta.tables import DeltaTable
from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") \
    .getOrCreate()

# 写入 Delta 表
df.write.format("delta").save("s3://my-bucket/orders/")

# MERGE(Upsert):存在则更新,不存在则插入
delta_table = DeltaTable.forPath(spark, "s3://my-bucket/orders/")
delta_table.alias("target").merge(
    updates_df.alias("source"),
    "target.order_id = source.order_id"
).whenMatchedUpdateAll() \
 .whenNotMatchedInsertAll() \
 .execute()

# 时间旅行:查询昨天的数据
spark.read.format("delta") \
    .option("timestampAsOf", "2026-04-25") \
    .load("s3://my-bucket/orders/").show()

# 查看历史版本
delta_table.history().show()

# 优化小文件 + Z-ORDER 聚类
spark.sql("OPTIMIZE delta.`s3://my-bucket/orders/` ZORDER BY (user_id, order_date)")

优势:与 Spark 集成最深,Databricks 平台原生支持,社区活跃,文档丰富。国内阿里云 EMR 也支持 Delta Lake。

劣势:多引擎支持不如 Iceberg(Trino/Presto 对 Delta 的支持相对滞后);事务日志在文件数量多时可能成为瓶颈。

Apache Iceberg

由 Netflix 开源(2018 年),现为 Apache 顶级项目,定位是"开放的表格式标准"。

核心机制:三层元数据结构——Catalog(表注册)→ Metadata File(表快照)→ Manifest List → Manifest File → 数据文件。每次写入生成新的 Metadata File,通过原子替换实现事务。

核心特性

  • 隐藏分区(Hidden Partitioning):分区逻辑对用户透明,查询时不需要写分区过滤条件,Iceberg 自动做分区裁剪。这是 Iceberg 相比 Hive 分区表最大的改进。
  • 分区演进(Partition Evolution):可以修改分区策略(如从按天分区改为按小时分区),历史数据不需要重写。
  • 行级删除(Row-level Delete):支持 Equality Delete 和 Position Delete,不需要重写整个文件。
  • 多引擎支持:Spark、Flink、Trino、Presto、Hive、StarRocks 都有原生支持,是多引擎架构的首选。
# Iceberg + Spark 示例
spark = SparkSession.builder \
    .config("spark.sql.extensions", "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions") \
    .config("spark.sql.catalog.my_catalog", "org.apache.iceberg.spark.SparkCatalog") \
    .config("spark.sql.catalog.my_catalog.type", "hive") \
    .getOrCreate()

# 建表(隐藏分区:按 order_date 的 day 分区,用户查询无感知)
spark.sql("""
    CREATE TABLE my_catalog.db.orders (
        order_id    STRING,
        user_id     BIGINT,
        amount      DECIMAL(10,2),
        order_date  TIMESTAMP
    ) USING iceberg
    PARTITIONED BY (days(order_date))
""")

# 查询时不需要指定分区,Iceberg 自动裁剪
spark.sql("""
    SELECT * FROM my_catalog.db.orders
    WHERE order_date BETWEEN '2026-04-01' AND '2026-04-25'
""")

# 修改分区策略(分区演进,历史数据无需重写)
spark.sql("""
    ALTER TABLE my_catalog.db.orders
    REPLACE PARTITION FIELD days(order_date) WITH hours(order_date)
""")

# 时间旅行
spark.sql("""
    SELECT * FROM my_catalog.db.orders
    FOR SYSTEM_TIME AS OF '2026-04-25 00:00:00'
""")

优势:多引擎生态最好,是开放标准的首选;隐藏分区和分区演进极大简化了分区管理;Flink 对 Iceberg 的支持非常完善,适合流批一体架构。

劣势:相比 Delta Lake,Databricks 平台上的集成度略低;元数据层次更复杂,调试难度稍高。

Apache Hudi

由 Uber 开源(2016 年),最早专注于解决数据库 CDC(Change Data Capture)同步到数据湖的问题。

两种存储类型

  • Copy-on-Write(COW):写入时直接重写受影响的 Parquet 文件,读性能好,写放大较高。适合读多写少的场景。
  • Merge-on-Read(MOR):写入时先写增量日志文件(Avro 格式),读时合并基础文件和日志文件。写性能极好,读时有合并开销。适合高频 Upsert 场景(如 CDC 同步)。
# Hudi + Spark 写入示例(MOR 模式,适合 CDC 场景)
df.write.format("hudi") \
    .option("hoodie.table.name", "orders") \
    .option("hoodie.datasource.write.recordkey.field", "order_id") \
    .option("hoodie.datasource.write.precombine.field", "updated_at") \
    .option("hoodie.datasource.write.operation", "upsert") \
    .option("hoodie.datasource.write.table.type", "MERGE_ON_READ") \
    .mode("append") \
    .save("s3://my-bucket/orders/")

优势:CDC 场景下的 Upsert 性能最好;MOR 模式对高频写入友好;与 Flink 集成支持近实时数据入湖(分钟级延迟)。

劣势:概念最复杂(COW/MOR、Timeline、Compaction 等),学习曲线陡峭;多引擎支持不如 Iceberg;社区活跃度近年有所下降。

三者选型建议

  • 重度使用 Databricks / Spark,团队技术栈单一:选 Delta Lake,集成最深,运维最简单
  • 多引擎架构(Spark + Flink + Trino 并用),追求开放标准:选 Iceberg,生态最广,未来兼容性最好
  • 核心需求是数据库 CDC 实时同步到数据湖,高频 Upsert:选 Hudi(MOR 模式),或考虑 Iceberg + Flink(近年来 Iceberg 在这个场景的支持也在快速完善)
  • 不确定怎么选:选 Iceberg,它是最接近"开放标准"的选项,未来切换引擎的代价最小

计算引擎层:数据怎么处理

Apache Spark:批处理首选

Spark 是数据湖计算层的主力引擎,适合复杂的批量 ETL、机器学习特征工程、大规模数据清洗。对 Delta Lake 和 Iceberg 的支持最成熟。

关键配置:

# 提交 Spark 作业(以 Iceberg 为例)
spark-submit \
  --packages org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.5.0 \
  --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions \
  --conf spark.sql.catalog.my_catalog=org.apache.iceberg.spark.SparkCatalog \
  --conf spark.sql.catalog.my_catalog.warehouse=s3://my-bucket/warehouse \
  my_etl_job.py

Flink 是流处理场景的首选引擎,对 Iceberg 和 Hudi 的写入支持很好,适合实时数据入湖(CDC 同步、实时特征计算结果写湖)。

Flink + Iceberg 的典型场景:MySQL CDC → Flink → Iceberg(数据湖),实现分钟级的数据库变更同步到数据湖,下游 Spark 或 Trino 可以查询近实时数据。

-- Flink SQL:实时将 MySQL CDC 数据写入 Iceberg
CREATE TABLE mysql_orders (
    order_id   STRING,
    user_id    BIGINT,
    status     STRING,
    updated_at TIMESTAMP(3),
    PRIMARY KEY (order_id) NOT ENFORCED
) WITH (
    'connector' = 'mysql-cdc',
    'hostname'  = 'mysql-host',
    'database-name' = 'shop',
    'table-name'    = 'orders'
);

CREATE TABLE iceberg_orders (
    order_id   STRING,
    user_id    BIGINT,
    status     STRING,
    updated_at TIMESTAMP(3),
    PRIMARY KEY (order_id) NOT ENFORCED
) WITH (
    'connector'          = 'iceberg',
    'catalog-name'       = 'my_catalog',
    'catalog-type'       = 'hive',
    'database-name'      = 'db',
    'table-name'         = 'orders',
    'warehouse'          = 's3://my-bucket/warehouse'
);

-- 实时同步:MySQL 的 INSERT/UPDATE/DELETE 实时反映到 Iceberg
INSERT INTO iceberg_orders SELECT * FROM mysql_orders;

Trino / Presto:交互式查询

Trino(原 PrestoSQL)是专为交互式查询设计的 MPP 引擎,查询延迟在秒级,适合数据分析师的即席查询(Ad-hoc Query)和 BI 报表。Trino 对 Iceberg 的支持最好,对 Delta Lake 的支持也在逐步完善。

Trino 的定位是"查询加速层",不做数据写入和复杂 ETL,专注于快速读取数据湖中的数据并返回结果。

-- Trino 查询 Iceberg 表(语法与标准 SQL 一致)
SELECT
    user_id,
    COUNT(*) AS order_cnt,
    SUM(amount) AS total_amount
FROM iceberg.db.orders
WHERE order_date >= DATE '2026-04-01'
GROUP BY user_id
ORDER BY total_amount DESC
LIMIT 100;

-- 时间旅行查询
SELECT * FROM iceberg.db.orders
FOR TIMESTAMP AS OF TIMESTAMP '2026-04-25 00:00:00';

StarRocks / Doris:OLAP 加速

StarRocks 和 Apache Doris 是国内广泛使用的实时 OLAP 引擎,可以直接查询数据湖(External Catalog),也可以将数据湖中的数据导入内部表获得更高查询性能。

StarRocks 对 Iceberg/Delta/Hudi 都有 External Catalog 支持,查询性能优于 Trino(向量化执行引擎),适合需要亚秒级响应的报表场景。

-- StarRocks 创建 Iceberg External Catalog
CREATE EXTERNAL CATALOG iceberg_catalog
PROPERTIES (
    "type" = "iceberg",
    "iceberg.catalog.type" = "hive",
    "hive.metastore.uris" = "thrift://hive-metastore:9083"
);

-- 直接查询数据湖(无需导入数据)
SELECT * FROM iceberg_catalog.db.orders
WHERE order_date = '2026-04-26'
LIMIT 100;

元数据管理层:数据怎么管理

元数据管理层负责记录"哪些表存在、表在哪里、表的 Schema 是什么",是数据湖的"目录"。

Hive Metastore(HMS)

最传统也最通用的元数据服务,几乎所有大数据引擎(Spark、Flink、Trino、Hive)都支持。HMS 将表的元数据存在 MySQL/PostgreSQL 中,通过 Thrift 协议提供服务。

HMS 是目前最安全的选择——生态支持最广,踩坑最少。缺点是不支持多版本元数据(无法回滚 Schema 变更)、并发写入时有锁竞争。

AWS Glue Data Catalog

AWS 托管的元数据服务,完全兼容 HMS API,在 AWS 生态内无缝集成(S3 + Glue + Athena + EMR)。不需要自己维护 Metastore 服务,适合全上 AWS 的团队。

Project Nessie

新一代的数据湖 Catalog,核心特性是类 Git 的版本控制:支持分支(Branch)、标签(Tag)、提交历史,可以在不同分支上独立开发和测试数据管道,确认无误后再合并到主分支。这对数据工程师来说极其有用——可以在测试分支上运行 ETL,不影响生产数据。Nessie 是 Iceberg 生态中的重要组件,Dremio 和 Databricks 都在推进 Nessie 的采用。

# Nessie 分支操作(类似 Git)
# 创建开发分支
curl -X POST http://nessie-server/api/v1/trees/branch/dev \
  -d '{"sourceRefName": "main"}'

# 在 dev 分支上运行 ETL,不影响 main
spark-submit --conf spark.sql.catalog.nessie.ref=dev my_etl.py

# 验证无误后合并到 main
curl -X POST http://nessie-server/api/v1/trees/branch/main/merge \
  -d '{"fromRefName": "dev"}'

典型组合方案

方案一:Databricks 全家桶

存储:S3/ADLS → 表格式:Delta Lake → 计算:Spark(Databricks Runtime)→ 元数据:Unity Catalog

适合:已采购 Databricks,追求开箱即用,不想管理基础设施。Delta Lake + Databricks 的组合在 OPTIMIZE/Z-ORDER/CDF 等功能上体验最好。

方案二:开源多引擎架构

存储:S3/OSS → 表格式:Iceberg → 计算:Spark(批处理)+ Flink(流处理)+ Trino(交互查询)→ 元数据:Hive Metastore

适合:技术团队强,追求开放生态,不想被单一厂商锁定。这是国内大厂(字节、美团、滴滴)的主流架构。

方案三:阿里云一体化

存储:OSS → 表格式:Iceberg 或 Delta Lake(EMR 都支持)→ 计算:EMR Spark + EMR Flink → 元数据:DLF(Data Lake Formation,阿里云托管 Metastore)→ 查询加速:MaxCompute 或 StarRocks

适合:业务在阿里云,希望减少自建组件,利用云厂商托管服务降低运维成本。

方案四:CDC 实时入湖

MySQL/PostgreSQL → Flink CDC → Iceberg(MOR 模式)→ Spark 定时 Compaction → Trino/StarRocks 查询

适合:需要将业务数据库的变更实时同步到数据湖,下游做近实时分析。Iceberg 的 Flink 支持在这个场景已经非常成熟。

总结

数据湖选型没有唯一答案,但有几个清晰的原则:

  • 存储层默认选云对象存储(S3/OSS),存算分离是趋势,除非有特殊合规要求
  • 表格式层优先考虑 Iceberg,多引擎生态最好,最接近开放标准;如果重度使用 Databricks 则选 Delta Lake
  • 计算引擎按场景分工:Spark 做批处理 ETL,Flink 做流式入湖,Trino/StarRocks 做交互查询
  • 元数据管理从 Hive Metastore 起步,稳定可靠;有多分支开发需求时考虑 Nessie

对于大多数团队,S3/OSS + Iceberg + Spark/Flink + HMS 是最稳健的起点,覆盖了 90% 的场景,且未来扩展灵活。不要一开始就追求最复杂的架构,先跑通核心链路,再根据实际瓶颈补充组件。