数据工程资源专题
Data Engineering Resources
🧭 学习路径
| 阶段 |
核心技能 |
工具 |
| 🟢 基础 |
SQL 精通、Python 数据处理 |
PostgreSQL、Pandas |
| 🟡 管道 |
ETL/ELT、工作流调度 |
Airflow、dbt、Prefect |
| 🟠 大数据 |
分布式计算、列式存储 |
Spark、Parquet、Delta Lake |
| 🔴 流处理 |
实时数据管道 |
Kafka、Flink、RisingWave |
| ⚫ 平台 |
数据湖、数据网格、MLOps |
Databricks、Snowflake |
数据是决策的原料。先保证数据质量 → 再谈分析深度。
📚 概述
数据工程是设计、构建和维护数据基础设施的学科,确保数据能够被有效采集、存储、处理和访问。数据工程师为数据科学家和分析师提供可靠的数据管道和平台。
核心职责:
- 数据管道设计与构建
- 数据仓库与数据湖架构
- ETL/ELT 流程开发
- 数据质量管理
- 实时数据处理
- 数据治理与安全
🗺️ 知识地图
graph TB
A[数据工程] --> B[数据采集]
A --> C[数据存储]
A --> D[数据处理]
A --> E[数据服务]
B --> B1[批处理采集]
B --> B2[流式采集]
B --> B3[API 集成]
B --> B4[CDC 变更捕获]
C --> C1[关系数据库]
C --> C2[NoSQL]
C --> C3[数据仓库]
C --> C4[数据湖]
D --> D1[批处理]
D --> D2[流处理]
D --> D3[数据转换]
D --> D4[数据质量]
E --> E1[数据 API]
E --> E2[BI 工具]
E --> E3[数据目录]
E --> E4[权限管理]
📖 经典书籍
入门级
| 书名 |
作者 |
年份 |
难度 |
特点 |
| 《Fundamentals of Data Engineering》 |
Joe Reis & Matt Housley |
2022 |
⭐⭐⭐ |
全面概览,最佳实践 |
| 《数据工程师手册》 |
王嘉诚 |
2021 |
⭐⭐⭐ |
中文实战,案例丰富 |
| 《Data Pipelines Pocket Reference》 |
James Densmore |
2021 |
⭐⭐⭐ |
精简实用 |
进阶级
| 书名 |
作者 |
年份 |
难度 |
特点 |
| 《Designing Data-Intensive Applications》 |
Martin Kleppmann |
2017 |
⭐⭐⭐⭐ |
DDIA,数据系统圣经 |
| 《The Data Warehouse Toolkit》 |
Ralph Kimball |
2013 |
⭐⭐⭐⭐ |
维度建模经典 |
| 《Streaming Systems》 |
Akidau et al. |
2018 |
⭐⭐⭐⭐ |
流处理权威指南 |
高级级
| 书名 |
作者 |
年份 |
难度 |
特点 |
| 《Database Internals》 |
Alex Petrov |
2019 |
⭐⭐⭐⭐⭐ |
数据库内部原理 |
| 《Data Mesh》 |
Zhamak Dehghani |
2022 |
⭐⭐⭐⭐⭐ |
下一代数据架构 |
| 《Building Evolutionary Architectures》 |
Ford et al. |
2017 |
⭐⭐⭐⭐⭐ |
演进式架构 |
🎓 在线课程
免费课程
| 课程 |
平台 |
机构 |
时长 |
链接 |
| Data Engineering on Google Cloud |
Coursera |
Google Cloud |
6 门课 |
链接 |
| Big Data Specialization |
Coursera |
UC San Diego |
8 门课 |
链接 |
| 大数据技术 |
中国大学 MOOC |
北京大学 |
12 周 |
链接 |
实战课程
| 课程 |
平台 |
特点 |
链接 |
| Data Engineer Nanodegree |
Udacity |
项目驱动,导师指导 |
链接 |
| Data Engineering with Python |
DataCamp |
交互式学习 |
链接 |
| Apache Spark 实战 |
Udemy |
Spark 深度讲解 |
链接 |
🔧 技术栈总览
数据采集
| 工具 |
类型 |
特点 |
链接 |
| Airbyte |
ELT 平台 |
开源,300+ 连接器 |
GitHub |
| Fivetran |
ELT 服务 |
托管服务,自动化 |
官网 |
| Debezium |
CDC |
实时变更捕获 |
GitHub |
| Logstash |
日志采集 |
ELK 栈组件 |
GitHub |
| Flume |
日志采集 |
Apache 项目 |
官网 |
数据存储
| 工具 |
类型 |
特点 |
链接 |
| PostgreSQL |
关系数据库 |
ACID,扩展性强 |
官网 |
| MongoDB |
文档数据库 |
灵活 Schema |
GitHub |
| Snowflake |
云数据仓库 |
弹性扩展,分离存储计算 |
官网 |
| Delta Lake |
数据湖格式 |
ACID 事务,版本控制 |
GitHub |
| Apache Iceberg |
数据湖格式 |
高性能,分区演进 |
官网 |
数据处理
| 工具 |
类型 |
特点 |
链接 |
| Apache Spark |
批/流处理 |
内存计算,生态完善 |
GitHub |
| Apache Flink |
流处理 |
低延迟,精确一次 |
GitHub |
| dbt |
数据转换 |
SQL 优先,版本控制 |
GitHub |
| Apache Beam |
统一批流 |
多引擎支持 |
官网 |
工作流编排
| 工具 |
类型 |
特点 |
链接 |
| Apache Airflow |
工作流调度 |
Python DAG,生态丰富 |
GitHub |
| Prefect |
工作流编排 |
现代 API,易调试 |
GitHub |
| Dagster |
数据编排 |
数据感知,开发体验好 |
GitHub |
| Kestra |
工作流自动化 |
YAML 定义,UI 友好 |
GitHub |
📊 核心技术详解
1. ETL 管道设计
# 使用 Prefect 构建 ETL 管道
from prefect import task, Flow, Parameter
from prefect.executors import LocalDaskExecutor
import pandas as pd
import sqlalchemy as sa
@task
def extract_data(source_url: str) -> pd.DataFrame:
"""从数据源提取数据"""
# 示例:从 CSV 或 API 提取
if source_url.endswith('.csv'):
df = pd.read_csv(source_url)
elif source_url.endswith('.json'):
df = pd.read_json(source_url)
else:
# 数据库连接
engine = sa.create_engine(source_url)
df = pd.read_sql("SELECT * FROM source_table", engine)
print(f"提取数据:{len(df)} 行,{len(df.columns)} 列")
return df
@task
def transform_data(df: pd.DataFrame) -> pd.DataFrame:
"""数据转换"""
# 数据清洗
df = df.drop_duplicates()
df = df.dropna(subset=['id', 'created_at'])
# 数据类型转换
df['created_at'] = pd.to_datetime(df['created_at'])
df['amount'] = df['amount'].astype(float)
# 业务逻辑
df['profit'] = df['revenue'] - df['cost']
df['profit_margin'] = df['profit'] / df['revenue']
# 数据验证
assert (df['amount'] >= 0).all(), "金额不能为负数"
assert (df['profit_margin'] <= 1).all(), "利润率不能超过 100%"
print(f"转换后数据:{len(df)} 行")
return df
@task
def load_data(df: pd.DataFrame, target_url: str, table_name: str):
"""加载数据到目标"""
engine = sa.create_engine(target_url)
# 增量加载或全量替换
df.to_sql(
table_name,
engine,
if_exists='replace', # 或 'append'
index=False,
method='multi',
chunksize=1000
)
print(f"数据已加载到 {table_name}")
@task
def send_notification(success: bool):
"""发送通知"""
if success:
print("✅ 数据管道执行成功")
# 可集成 Slack、邮件等通知
else:
print("❌ 数据管道执行失败")
# 定义工作流
with Flow("ETL Pipeline", executor=LocalDaskExecutor()) as flow:
source_url = Parameter("source_url", default="postgresql://user:pass@host/db")
target_url = Parameter("target_url", default="postgresql://user:pass@host/warehouse")
table_name = Parameter("table_name", default="fact_sales")
raw_data = extract_data(source_url)
cleaned_data = transform_data(raw_data)
load_result = load_data(cleaned_data, target_url, table_name)
# 成功通知
send_notification(True)
# 运行管道
if __name__ == "__main__":
flow.register(project_name="Data Engineering")
# flow.run()
2. 数据仓库建模
# Kimball 维度建模示例
# 事实表 + 维度表设计
"""
-- 事实表:销售事实
CREATE TABLE fact_sales (
sale_id BIGINT PRIMARY KEY,
date_id INTEGER NOT NULL,
customer_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
store_id INTEGER NOT NULL,
-- 度量
quantity INTEGER NOT NULL,
unit_price DECIMAL(10, 2) NOT NULL,
total_amount DECIMAL(12, 2) NOT NULL,
cost DECIMAL(12, 2) NOT NULL,
profit DECIMAL(12, 2) GENERATED ALWAYS AS (total_amount - cost) STORED,
-- 外键
FOREIGN KEY (date_id) REFERENCES dim_date(date_id),
FOREIGN KEY (customer_id) REFERENCES dim_customer(customer_id),
FOREIGN KEY (product_id) REFERENCES dim_product(product_id),
FOREIGN KEY (store_id) REFERENCES dim_store(store_id)
);
-- 维度表:日期维度
CREATE TABLE dim_date (
date_id INTEGER PRIMARY KEY,
full_date DATE NOT NULL,
year INTEGER,
quarter INTEGER,
month INTEGER,
month_name VARCHAR(20),
day_of_month INTEGER,
day_of_week INTEGER,
day_name VARCHAR(20),
is_weekend BOOLEAN,
is_holiday BOOLEAN
);
-- 维度表:客户维度 (SCD Type 2)
CREATE TABLE dim_customer (
customer_key SERIAL PRIMARY KEY,
customer_id INTEGER NOT NULL,
-- 属性
customer_name VARCHAR(100),
email VARCHAR(100),
phone VARCHAR(20),
address VARCHAR(200),
city VARCHAR(50),
country VARCHAR(50),
segment VARCHAR(50),
-- SCD Type 2 元数据
effective_date DATE NOT NULL,
expiration_date DATE,
is_current BOOLEAN DEFAULT TRUE,
version INTEGER DEFAULT 1,
UNIQUE (customer_id, effective_date)
);
-- 维度表:产品维度
CREATE TABLE dim_product (
product_key SERIAL PRIMARY KEY,
product_id INTEGER NOT NULL,
product_name VARCHAR(200),
category VARCHAR(50),
subcategory VARCHAR(50),
brand VARCHAR(50),
sku VARCHAR(50),
cost_price DECIMAL(10, 2),
list_price DECIMAL(10, 2),
is_active BOOLEAN DEFAULT TRUE
);
"""
# 使用 dbt 进行数据转换
# models/fact_sales.sql
"""
{{
config(
materialized='incremental',
unique_key='sale_id',
incremental_strategy='merge'
)
}}
WITH source AS (
SELECT * FROM {{ source('raw', 'sales') }}
),
dim_date_lookup AS (
SELECT * FROM {{ ref('dim_date') }}
),
dim_customer_lookup AS (
SELECT * FROM {{ ref('dim_customer') }} WHERE is_current = TRUE
),
final AS (
SELECT
{{ dbt_utils.generate_surrogate_key(['s.sale_id']) }} AS sale_key,
d.date_id,
c.customer_key,
p.product_key,
s.store_id,
s.quantity,
s.unit_price,
s.quantity * s.unit_price AS total_amount,
s.cost,
(s.quantity * s.unit_price) - s.cost AS profit,
CURRENT_TIMESTAMP AS created_at
FROM source s
JOIN dim_date_lookup d ON DATE(s.sale_date) = d.full_date
JOIN dim_customer_lookup c ON s.customer_id = c.customer_id
JOIN {{ ref('dim_product') }} p ON s.product_id = p.product_id
{% if is_incremental() %}
WHERE s.sale_date > (SELECT MAX(full_date) FROM {{ ref('dim_date') }})
{% endif %}
)
SELECT * FROM final
"""
3. 实时数据处理
# Apache Flink 流处理示例
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.table import StreamTableEnvironment, EnvironmentSettings
def flink_streaming_pipeline():
"""Flink 实时数据处理"""
# 创建执行环境
env = StreamExecutionEnvironment.get_execution_environment()
settings = EnvironmentSettings.new_instance().in_streaming_mode().use_blink_planner().build()
table_env = StreamTableEnvironment.create(env, environment_settings=settings)
# 定义源表 (Kafka)
table_env.execute_sql("""
CREATE TABLE source_orders (
order_id BIGINT,
user_id BIGINT,
amount DOUBLE,
status STRING,
order_time TIMESTAMP(3),
WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH (
'connector' = 'kafka',
'topic' = 'orders',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'order-analysis',
'format' = 'json',
'scan.startup.mode' = 'latest-offset'
)
""")
# 定义结果表
table_env.execute_sql("""
CREATE TABLE hourly_stats (
window_start TIMESTAMP(3),
window_end TIMESTAMP(3),
total_orders BIGINT,
total_amount DOUBLE,
avg_amount DOUBLE,
unique_users BIGINT
) WITH (
'connector' = 'print'
)
""")
# 实时聚合查询
table_env.execute_sql("""
INSERT INTO hourly_stats
SELECT
TUMBLE_START(order_time, INTERVAL '1' HOUR) AS window_start,
TUMBLE_END(order_time, INTERVAL '1' HOUR) AS window_end,
COUNT(*) AS total_orders,
SUM(amount) AS total_amount,
AVG(amount) AS avg_amount,
COUNT(DISTINCT user_id) AS unique_users
FROM source_orders
WHERE status = 'completed'
GROUP BY TUMBLE(order_time, INTERVAL '1' HOUR)
""")
# 实时异常检测 (CEP)
table_env.execute_sql("""
CREATE TABLE suspicious_orders (
user_id BIGINT,
order_count BIGINT,
total_amount DOUBLE,
window_start TIMESTAMP(3),
window_end TIMESTAMP(3)
) WITH (
'connector' = 'kafka',
'topic' = 'suspicious-orders',
'properties.bootstrap.servers' = 'localhost:9092',
'format' = 'json'
)
""")
# 检测 5 分钟内下单超过 10 次且总金额超过 10000 的用户
table_env.execute_sql("""
INSERT INTO suspicious_orders
SELECT
user_id,
COUNT(*) AS order_count,
SUM(amount) AS total_amount,
TUMBLE_START(order_time, INTERVAL '5' MINUTE),
TUMBLE_END(order_time, INTERVAL '5' MINUTE)
FROM source_orders
GROUP BY user_id, TUMBLE(order_time, INTERVAL '5' MINUTE)
HAVING COUNT(*) > 10 AND SUM(amount) > 10000
""")
# 运行流处理
if __name__ == "__main__":
flink_streaming_pipeline()
4. 数据质量管理
# 使用 Great Expectations 进行数据质量检查
import great_expectations as ge
from great_expectations.core.expectation_configuration import ExpectationConfiguration
from great_expectations.data_context import DataContext
def setup_data_quality_framework():
"""设置数据质量框架"""
# 初始化 DataContext
context = DataContext()
# 创建批次
batch = context.get_batch(
batch_kwargs={"table": "fact_sales", "schema": "public"},
expectation_suite_name="sales_quality_suite"
)
# 定义期望
expectations = [
# 非空检查
ExpectationConfiguration(
expectation_type="expect_column_values_to_not_be_null",
kwargs={"column": "sale_id"}
),
ExpectationConfiguration(
expectation_type="expect_column_values_to_not_be_null",
kwargs={"column": "total_amount"}
),
# 值域检查
ExpectationConfiguration(
expectation_type="expect_column_values_to_be_between",
kwargs={"column": "quantity", "min_value": 0}
),
ExpectationConfiguration(
expectation_type="expect_column_values_to_be_between",
kwargs={"column": "unit_price", "min_value": 0}
),
# 唯一性检查
ExpectationConfiguration(
expectation_type="expect_column_values_to_be_unique",
kwargs={"column": "sale_id"}
),
# 分布检查
ExpectationConfiguration(
expectation_type="expect_column_mean_to_be_between",
kwargs={"column": "profit_margin", "min_value": 0, "max_value": 1}
),
# 完整性检查
ExpectationConfiguration(
expectation_type="expect_table_row_count_to_be_between",
kwargs={"min_value": 1000}
),
# 业务规则检查
ExpectationConfiguration(
expectation_type="expect_compound_columns_to_be_unique",
kwargs={"column_list": ["order_id", "product_id"]}
)
]
# 添加期望到批次
for exp in expectations:
batch.add_expectation(exp)
# 验证数据
results = batch.validate()
# 生成报告
if results.success:
print("✅ 数据质量检查通过")
else:
print("❌ 数据质量检查失败")
for result in results.results:
if not result.success:
print(f" - {result.expectation_config.expectation_type}: {result.expectation_config.kwargs}")
return results
# 使用 dbt 测试
# models/schema.yml
"""
version: 2
models:
- name: fact_sales
columns:
- name: sale_id
tests:
- unique
- not_null
- name: total_amount
tests:
- not_null
- dbt_utils.accepted_range:
min_value: 0
- name: quantity
tests:
- dbt_utils.accepted_range:
min_value: 0
integer: true
- name: profit_margin
tests:
- dbt_utils.accepted_range:
min_value: 0
max_value: 1
- name: customer_id
tests:
- relationships:
to: ref('dim_customer')
field: customer_key
"""
5. 数据目录与治理
# 使用 Apache Atlas 进行数据治理
from atlasclient.client import Atlas
def setup_data_governance():
"""设置数据治理框架"""
# 连接 Atlas
atlas = Atlas(
host='localhost',
port=21000,
username='admin',
password='admin'
)
# 创建数据资产
table_entity = {
"typeName": "DataSet",
"attributes": {
"qualifiedName": "warehouse.sales.fact_sales",
"name": "fact_sales",
"description": "销售事实表,包含所有交易记录",
"owner": "data-engineering-team",
"schema": [
{"name": "sale_id", "type": "BIGINT", "description": "销售 ID"},
{"name": "date_id", "type": "INTEGER", "description": "日期 ID"},
{"name": "customer_id", "type": "INTEGER", "description": "客户 ID"},
{"name": "total_amount", "type": "DECIMAL", "description": "总金额"},
{"name": "profit", "type": "DECIMAL", "description": "利润"}
],
"classification": ["PII", "BUSINESS_CRITICAL"],
"tags": ["sales", "fact-table", "daily-refresh"]
}
}
# 创建血缘关系
lineage_entity = {
"typeName": "Process",
"attributes": {
"qualifiedName": "etl.sales_pipeline",
"name": "Sales ETL Pipeline",
"inputs": [
{"typeName": "DataSet", "uniqueAttributes": {"qualifiedName": "raw.sales"}}
],
"outputs": [
{"typeName": "DataSet", "uniqueAttributes": {"qualifiedName": "warehouse.sales.fact_sales"}}
],
"description": "每日销售数据 ETL 流程"
}
}
# 上传实体
atlas.entities.create(entities=[table_entity, lineage_entity])
print("✅ 数据资产和血缘关系已创建")
# 数据发现与搜索
def search_data_assets(search_query: str):
"""搜索数据资产"""
atlas = Atlas(host='localhost', port=21000, username='admin', password='admin')
results = atlas.search_dsl(query=search_query)
print(f"找到 {len(results)} 个数据资产:")
for entity in results:
print(f" - {entity.name} ({entity.typeName})")
print(f" 描述:{entity.description}")
print(f" 标签:{', '.join(entity.tags)}")
return results
📝 最佳实践
数据管道设计原则
data_pipeline_principles = {
'可靠性': [
'幂等性:多次执行产生相同结果',
'容错性:失败后自动重试',
'监控:关键指标告警',
'审计:操作日志记录'
],
'可扩展性': [
'水平扩展:支持分布式处理',
'增量处理:仅处理变化数据',
'分区策略:按时间/业务分区',
'资源隔离:关键任务优先'
],
'可维护性': [
'代码版本控制:Git 管理',
'配置外部化:环境分离',
'文档完善:数据字典、血缘',
'测试覆盖:单元测试、集成测试'
],
'数据质量': [
'入口校验:Schema 验证',
'过程监控:质量指标',
'出口检查:完整性验证',
'异常处理:死信队列'
]
}
for principle, practices in data_pipeline_principles.items():
print(f"\n{principle}:")
for practice in practices:
print(f" • {practice}")
性能优化技巧
# 1. 批处理优化
batch_optimization_tips = """
- 批量写入:使用 batch insert 而非单条插入
- 并行处理:多进程/多线程并行
- 分区剪枝:只读取需要的分区
- 列式存储:使用 Parquet/ORC 格式
- 数据压缩:Snappy/Zstd 压缩
"""
# 2. SQL 优化
sql_optimization_tips = """
- 避免 SELECT *:只选择需要的列
- 使用分区字段过滤
- 合理使用索引
- 避免笛卡尔积
- 物化常用中间结果
"""
# 3. Spark 优化
spark_optimization_tips = """
- 调整 executor 内存和核心数
- 使用 broadcast join 处理小表
- 缓存重复使用的 DataFrame
- 避免数据倾斜 (salting 技术)
- 使用 Tungsten 引擎
"""
print("数据工程性能优化技巧:")
print("=" * 60)
print(batch_optimization_tips)
print(sql_optimization_tips)
print(spark_optimization_tips)
🎯 学习路径
入门阶段 (1-3 个月)
第 1 月:编程与 SQL 基础
- Python 高级编程
- SQL 进阶 (窗口函数、CTE)
- Linux 基础命令
- Git 版本控制
第 2 月:数据库与 ETL 基础
- 关系数据库原理
- ETL 设计模式
- Apache Airflow 基础
- 数据仓库概念
第 3 月:大数据基础
- Hadoop 生态系统
- Apache Spark 基础
- 分布式计算原理
- 第一个数据管道项目
进阶阶段 (3-6 个月)
第 4-5 月:流处理与云原生
- Apache Kafka
- Apache Flink
- 云数据平台 (AWS/GCP/Azure)
- dbt 数据转换
第 6 月:数据治理与架构
- 数据建模 (维度建模)
- 数据质量管理
- 数据治理框架
- 数据湖架构
📚 中文资源
博客与社区
| 资源 |
类型 |
链接 |
| 数据工程实战 |
公众号 |
链接 |
| 大数据工匠 |
博客 |
链接 |
| DataFun |
社区 |
链接 |
视频课程
| 课程 |
讲师 |
平台 |
链接 |
| 大数据入门 |
尚硅谷 |
B 站 |
链接 |
| Spark 进阶 |
李兴华 |
慕课网 |
链接 |
最后更新: 2026-06-01
相关文档:
- big-data-business-resources.md - 大数据与商业分析
- sql-resources.md - SQL 与数据库分析
- machine-learning-resources.md - 机器学习资源
**数据工程资源专题 | 构建可靠的数据基础设施**
[返回顶部](#数据工程资源专题)