ETL 基础与工具选型:别在开头就选错路

数据整合听起来高大上,其实就是把数据从 A 搬到 B,再洗一洗、切一切。很多团队一上来就纠结「用 Spark 还是 Flink」,结果半年过去了,连数据都没跑通。我见过太多这样的项目了。

这一章,咱们不绕弯子。直接聊清楚四件事:ETL 和 ELT 到底怎么选?批处理和流处理分别解决什么问题?主流工具哪些值得上手?

数据湖 ETL 管线工具选型框架 原始数据源 数据湖(统一存储层) 传统 ETL 先转换,后加载 适合高合规场景 现代 ELT 先加载,后转换 适合数据湖/云原生 批处理 流处理 批处理 流处理 Spark / Airflow Flink / Kafka Streams dbt / Glue Kafka / Flink

ETL 还是 ELT?哪套更适合你

说白了,传统 ETL 是「先做饭再端菜」——数据在进入目标系统之前,先完成清洗、转换、聚合。ELT 则是「先上菜再加工」——数据原封不动地先堆进数据湖,什么时候用什么时候再转换。

核心判断依据:

  • ETL 适合:监管严格、数据量可控、目标库写入性能敏感(比如传统数仓)
  • ELT 适合:数据湖架构、云原生、数据量大但写入带宽充裕(比如 S3 + Presto)

我在早期的一个医疗项目里,就吃过 ETL 的亏。当时非要把所有数据都标准化后再加载,结果下游一直变字段格式,ETL 脚本反复重写。后来换成了 ELT,先落湖再通过视图做转换,迭代速度快了两倍不止。

批处理 vs 流处理:不是非此即彼

很多人以为批处理和流处理是互斥的。其实不是。你想想看,大多数场景白天跑流处理做近实时分析,晚上跑批处理做全量校对和回溯。我建议先想清楚你的数据延迟要求:

场景 推荐模式 延迟要求
报表、月度统计 批处理 小时级 / 天级
实时监控、异常检测 流处理 秒级
多组学特征提取 批 + 流混合 分钟级 ~ 天级
基因变异实时分析 流处理为主 秒级 ~ 分钟级

避坑指南:我曾经在一个转录组项目里过度追求「全实时」,结果存储成本翻了两番,实际科学家们只关心 T+1 的差异表达分析。后来我把大部分管线改回批处理,只保留一个很小的流通道做质控告警,效果反而更好。

常用工具横向对比

下面这几个工具,基本覆盖了 90% 的数据湖 ETL 场景。我按自己的使用体验排了序:

Apache Spark

Spark 是大数据批处理的事实标准。它的 Structured Streaming 也能做流处理。我用 Spark 做基因组数据清洗特别顺手——一次读取数万个小文件,性能远超 Python 原生方法。

from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("multiomics_etl").getOrCreate()

# 读取多个表达矩阵文件
df = spark.read.option("header", "true").option("sep", "\t") \
    .csv("s3://omics-landing/expression/*.tsv")

# 过滤低表达基因
df_clean = df.filter(df.expression_value > 0.1)

# 写出到 Parquet
df_clean.write.mode("overwrite").parquet("s3://omics-lake/expression_clean/")

个人经验:Spark 的 shuffle 是瓶颈。多组学数据经常涉及样本矩阵转置,这时候记得调大 spark.sql.shuffle.partitions,我一般设为核心数的 3 倍。

Apache Airflow

Airflow 不是真正的 ETL 引擎,它是任务编排器。但数据管线里的依赖关系、重试、告警,全靠它兜底。

from airflow import DAG
from airflow.providers.apache.spark.operators.spark_submit import SparkSubmitOperator
from datetime import datetime

default_args = {"owner": "omics_team", "retries": 2}

with DAG(
    dag_id="multiomics_pipeline",
    start_date=datetime(2024, 1, 1),
    schedule_interval="0 2 * * *",
    default_args=default_args,
    catchup=False
) as dag:

    extract_rnaseq = SparkSubmitOperator(
        task_id="extract_rnaseq",
        application="/opt/etl/scripts/extract_rnaseq.py",
        conn_id="spark_default"
    )

    extract_proteomics = SparkSubmitOperator(
        task_id="extract_proteomics",
        application="/opt/etl/scripts/extract_proteomics.py",
        conn_id="spark_default"
    )

    [extract_rnaseq, extract_proteomics] >> transform_merge

我记得有一次某个上游数据源的 API 改了格式,Airflow 自动重试了 3 次都没成功,直接给我发短信了。这种兜底能力,在无人值守的半夜管线里特别重要。

dbt(data build tool)

dbt 是 ELT 模式里的明星。它只管「T」——转换。数据已经落在湖里了,你用 SQL 定义模型,dbt 帮你自动管理依赖和增量更新。

-- models/expression_summary.sql
-- 对单细胞表达矩阵做汇总

WITH cell_type_agg AS (
    SELECT
        cell_type,
        avg(log2_expression) AS mean_expression,
        count(DISTINCT gene_id) AS n_genes_detected
    FROM {{ ref('raw_expression') }}
    GROUP BY cell_type
)
SELECT * FROM cell_type_agg

小建议:dbt 的增量模式在组学数据上很好用。每次只处理新追加的样本,不会每次都全量重新计算。配合 dbt 的测试功能,还能自动检查数据质量——比如检测到某个基因表达突然为零,直接告警。

AWS Glue

Glue 是 AWS 的全托管 ETL 服务。如果你整个基础架构都在 AWS 上,它会是性价比很高的选择。Serverless,不用维护集群。

import sys
from awsglue.transforms import *
from awsglue.context import GlueContext
from awsglue.job import Job

glueContext = GlueContext(spark.sparkContext)
job = Job(glueContext)
job.init(args["JOB_NAME"], args)

# 从 Glue Data Catalog 读取
datasource = glueContext.create_dynamic_frame.from_catalog(
    database="omics_metadata",
    table_name="proteomics_raw"
)

# 过滤空值
filtered = Filter.apply(frame=datasource, f=lambda x: x["intensity"] is not None)

# 写出到 Parquet 分区表
glueContext.write_dynamic_frame.from_options(
    frame=filtered,
    connection_type="s3",
    connection_options={"path": "s3://omics-lake/proteomics/"},
    format="parquet"
)
job.commit()

注意:Glue 的 Spark 版本比社区版落后半年到一年。如果你用很新的 Delta Lake 或 Iceberg 特性,可能会碰上兼容性问题。我在一个项目里就因为这个不得不回退到纯 Spark 方案。

工具选型速查表

工具 适用场景 上手成本 多组学优势
Spark 大规模批处理、ETL 中 支持 Python/Scala/R,生态丰富
Airflow 管线编排、依赖管理 低 适合多步骤组学流程
dbt ELT 中的转换层 低 模型管理和数据测试强
AWS Glue AWS 原生全托管 低 无需运维、集成 Data Catalog

我的习惯做法是:Airflow 做编排,Spark 做批处理 ETL,dbt 做湖内转换层。这套组合在大多数多组学项目里都能跑得很好。如果预算宽裕、团队不大,Glue 也是一个不错的选择,尤其适合只用 AWS 的场景。

核心总结:

  • 严格合规 + 小数据量 → 选传统 ETL
  • 数据湖 + 大数据量 → 选 ELT
  • 延迟要求秒级以下 → 必须上流处理
  • 延迟分钟级到天级 → 批处理更省成本
  • 工具没有银弹,先跑通再优化