DeltaLake是一个开源的存储层,它为大数据的读写带来了ACID的能力,通过快照隔离机制为HDFS提供了读写一致性的保证,同时DeltaLake提供内部版本的跟踪更能,使得用户可以轻松进行快照、版本回滚。
数据湖是近些年提出的新的数据架构,将大量的数据存储到数据湖中,数据湖可以认为是一个可以无限扩展的存储和计算架构。然而在数据湖实践落地的过程中也存在以下问题
- 数据质量问题,数据进入数据湖由于没有任何数据格式校验过程,所以经常出现数据值缺失或者数据错误的情况
- 数据稳定性问题,与传统关系性数据库相比,大数据缺少事务管理特性,所以会出现数据不一致的情况,通常要进行数据的重新处理
- 性能问题,随着数据湖中数据的不断增加,文件和目录的数量也会大量的增加,数据作业在处理元数据上实践花费的实践越来越多,性能越来越差
- 数据更新问题,现在的数据仓库存储结构主要是基于分区级别更新的,不支持更细颗粒度的更新
DeltaLake架构提供的功能可以很好的解决目前数据湖存储架构的问题
- ACID,支持强级别的隔离机制保证数据的一致性,每次写入都同时都会添加一个新的数据文件,并为这次写入添加一个log文件,Log文件会记录这次数据的变更,这样做的目的可以保证数据变更的原子性,读写操作保持独立。
- 可扩展的元数据管理,元数据以文件的形式进行管理,元数据处理方式同数据文件一样处理,因此通过分布式的处理方式,可以轻松管理上百万个分区
- 流批一体,支持流批一体的处理方式,通过一套代码对批流进行同意处理,避免了同逻辑两套代码的重复成本投入
- Schema管理,支持Schema的进化和变化管理,支持增减字段
- 数据修改,支持行级别的merge、upsert和delete操作,同是也支持一些复杂的予以处理比如CDC
spark shell
通过spark直接操作delta
bin/spark-shell --packages io.delta:delta-core_2.12:0.7.0 \
--conf "spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension" \
--conf "spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog"
创建表
通过Spark API创建表
val df = spark.range(10)
df.write.format("delta").mode(SaveMode.Overwrite).save("file:///tmp/delta/testdata")
通过Sql创建表,Spark需要打开HiveSupport开关
spark.sql("create table testdata (id int) using delta")
spark.sql("insert into testdata values (1),(2),(3),(4)")
更新数据
通过Spark API更新数据
val table = DeltaTable.forPath("/tmp/delta/testdata") table.update(col("id").equalTo(1), Map("id"-> lit("100")))
通过Sql更新数据
spark.sql("update testdata set id=100 where id=1")
删除数据
通过Spark API删除数据
val table = DeltaTable.forPath("/tmp/delta/testdata")
table.delete(col("id").equalTo(100))
通过Sql删除数据
spark.sql("delete from testdata where id=100")
字段增加
通过SparkAPI进行merge时,updateAll和insertAll会自动添加字段,insert/update是可以的,自动更新需要添加一个Spark的配置"spark.databricks.delta.schema.autoMerge.enabled"=true
val newData = spark.createDataFrame(spark.sparkContext.parallelize(Seq((2, "SH"), (30, "ZD"))))
.toDF("id", "city")
table.as("old").merge(
newData.as("new"),
"new.id==old.id")
.whenMatched
.updateAll()
.whenNotMatched()
.insertAll()
.execute()
SQL添加字段
spark.sql("alter table testdata add columns (city string)")
字段修改删除
Deltalake只支持字段的备注和顺序的修改,修改字段类型或者删除字段需要重刷数据。
alter table testdata change city city string comment 'city' first"
Kafka流式数据处理
从kafka topic中读取数据保存到Delta Lake中
val df = spark.readStream.format("kafka")
.option("kafka.bootstrap.servers", "a80.kd:9092")
.option("subscribe", "updates")
.load()
import spark.implicits._
val schema = new StructType()
.add($"uid".int)
.add($"email".string)
val output = df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)").as[(String, String)]
.select(from_json(col("value"), schema).alias("dd"))
.select("dd.uid", "dd.email")
.writeStream
.format("delta")
.outputMode("append")
.option("checkpointLocation", "file:///tmp/delta/events/_checkpoints/etl-from-json")
.start("file:///tmp/delta/events")
output.awaitTermination()
表历史
显示表历史记录
val table = DeltaTable.forPath("/tmp/delta/events")
table.history().show()
时间旅行,查看历史中某个版本的记录
val df = spark.read.format("delta").option("versionAsOf", 0).load("/tmp/delta/events")
df.show()