一、为什么需要数据湖
传统数据仓库的局限性:
- 结构化数据为主,难以处理非结构化数据
- Schema固定,难以适应业务变化
- 数据预处理后丢失细节
- 存储成本高
数据湖的特点:
- 存储各种类型数据(结构化、半结构化、非结构化)
- Schema-on-Read(读时模式)
- 保留原始数据,可重复计算
- 降低数据存储成本
二、数据湖架构
1. 核心组件
数据源
├── 日志系统(Kafka)
├── 业务数据库(MySQL/PostgreSQL)
├── 埋点数据(App/Web)
├── 外部API
└── 文件上传(S3/HDFS)
↓
数据采集层
├── Flume(日志)
├── Debezium(CDC)
├── Sqoop(批量)
└── Kafka Connect
↓
数据存储层
├── 对象存储(MinIO/S3/HDFS)
├── Delta Lake/Iceberg/Hudi
└── 数据目录(Apache Atlas)
↓
数据处理层
├── Spark
├── Flink
└── Presto/Trino
↓
数据应用层
├── BI报表
├── 数据科学
└── ML平台
2. 技术选型对比
| 组件 | 选项 | 推荐 |
|---|---|---|
| 存储 | HDFS/S3/MinIO | S3(云)/MinIO(私有) |
| 格式 | Parquet/ORC/Avro | Parquet |
| 表格式 | Delta/Iceberg/Hudi | Delta Lake |
| 查询引擎 | Presto/Trino/Spark | Trino |
| 元数据 | Hive Metastore/Glue | Hive Metastore |
三、Delta Lake实战
1. Delta Lake简介
Delta Lake是Databricks开源的存储层,提供:
- ACID事务
- 可扩展元数据处理
- 时间旅行(Time Travel)
- 模式强制和演化
- 流批一体
2. Spark + Delta Lake
依赖配置:
xml
<dependency>
<groupId>io.delta</groupId>
<artifactId>delta-core_2.12</artifactId>
<version>2.4.0</version>
</dependency>
写入数据:
python
from delta.tables import DeltaTable
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.appName("DataLakeDemo") \
.config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") \
.config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog") \
.getOrCreate()
# 批量写入
df = spark.read.format("json").load("/data/events/*.json")
df.write \
.format("delta") \
.mode("overwrite") \
.partitionBy("date", "event_type") \
.option("mergeSchema", "true") \
.save("/delta/events")
流式写入:
python
# Kafka -> Delta Lake
streaming_df = spark.readStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "localhost:9092") \
.option("subscribe", "events") \
.load()
query = streaming_df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)") \
.writeStream \
.format("delta") \
.option("checkpointLocation", "/delta/events/_checkpoints") \
.outputMode("append") \
.start("/delta/events")
读取数据:
python
# 读取最新数据
df = spark.read.format("delta").load("/delta/events")
# 时间旅行 - 读取历史版本
df_v1 = spark.read \
.format("delta") \
.option("versionAsOf", 1) \
.load("/delta/events")
# 时间旅行 - 读取指定时间点
df_before = spark.read \
.format("delta") \
.option("timestampAsOf", "2024-01-01T00:00:00") \
.load("/delta/events")
增量读取:
python
# 获取增量数据
deltaTable = DeltaTable.forPath(spark, "/delta/events")
# 只读取新数据
newDF = deltaTable.toDF().filter(col("date") >= "2024-01-15")
3. UPSERT操作
python
from delta.tables import DeltaTable
# Merge操作(UPSERT)
deltaTable = DeltaTable.forPath(spark, "/delta/users")
deltaTable.alias("old").merge(
updatesDF.alias("new"),
"old.user_id = new.user_id"
).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute()
4. 数据优化
python
# VACUUM - 删除不需要的文件(保留7天)
deltaTable.vacuum(retentionHours = 168)
# OPTIMIZE - 优化小文件
deltaTable.optimize().where("date = '2024-01-15'").zOrderBy("user_id").execute()
四、Hudi实战
1. Hudi简介
Hudi(Hadoop Upsert Delete and Incremental)特点:
- 支持UPSERT/DELETE
- 增量拉取
- 多种表类型(Copy on Write / Merge on Read)
2. Spark + Hudi
python
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.appName("HudiDemo") \
.config("spark.serializer", "org.apache.spark.serializer.KryoSerializer") \
.config("spark.sql.extensions", "org.apache.spark.sql.hudi.HoodieSparkSessionExtension") \
.getOrCreate()
# 写入数据
hoodie_options = {
'hoodie.table.name': 'events',
'hoodie.datasource.write.recordkey.field': 'id',
'hoodie.datasource.write.partitionpath.field': 'date',
'hoodie.datasource.write.table.type': 'COPY_ON_WRITE',
'hoodie.datasource.write.operation': 'bulk_insert',
'hoodie.datasource.write.precombine.field': 'ts',
'hoodie.upsert.shuffle.parallelism': '200',
'hoodie.insert.shuffle.parallelism': '200'
}
df.write \
.format("hudi") \
.options(**hoodie_options) \
.mode("append") \
.save("hdfs://namenode:8020/hudi/events")
3. 增量拉取
python
# 增量拉取
spark.read \
.format("hudi") \
.load("hdfs://namenode:8020/hudi/events") \
.createOrReplaceTempView("hudi_events_snapshot")
# 获取指定时间点后的数据
incremental_df = spark.sql("""
SELECT * FROM hudi_events_snapshot
WHERE hoodie_commit_time > '20240115120000'
""")
五、数据湖最佳实践
1. 表设计
sql
-- 使用分区表
CREATE TABLE events (
id BIGINT,
user_id BIGINT,
event_type STRING,
properties STRING,
event_time TIMESTAMP
)
USING delta
PARTITIONED BY (date STRING, event_type STRING)
LOCATION '/delta/events'
-- 配置Z-Order优化
OPTIMIZE events
WHERE date = '2024-01-15'
ZORDER BY (user_id, event_time)
2. 数据治理
python
# 数据质量检查
from great_expectations import GreatExpectations
context = GreatExpectations()
checkpoint = context.get_checkpoint("events_quality")
results = checkpoint.run(
batch_request={
"datasource_name": "my_datasource",
"data_asset_name": "events",
}
)
if not results["success"]:
# 发送告警
send_alert(results["failed_expectations"])
3. 权限控制
sql
-- 基于列的权限控制
GRANT SELECT(event_time, event_type) ON events TO analyst_role;
GRANT SELECT ON events TO data_scientist_role;
-- 基于行的权限控制
CREATE TABLE events_filtered AS
SELECT * FROM events
WHERE CASE
WHEN current_user() = 'admin' THEN true
ELSE date >= '2024-01-01'
END
六、总结
数据湖是现代数据平台的核心:
- Lakehouse:结合数据湖和数据仓库优点
- Delta Lake:成熟的表格式,支持ACID
- Hudi:适合CDC场景,支持增量处理
- 最佳实践:分区、Z-Order、数据质量
实施建议:
- 评估数据量和业务需求
- 选择合适的表格式
- 设计合理的分区策略
- 建立数据质量监控
个人观点,仅供参考