Delta Lake的调研报告

Delta Lake的调研报告

参考:DeltaLake官网,Delta 初探DeltaLakeDeltaLake详解阿里文档

架构图

在这里插入图片描述

Delta Lake简介

在说 Delta Lake 之前,要先提一下 Data Lake ,Data Lake 的主要思想是将企业中的所有数据进行统一管理。例如基于 Hadoop 的 Data Lake 方案可以非常低成本的存储所有类型的数据。
基于 hadoop 的方案只支持批量插入,且用户读取时可能无法获取最新数据,多用户同时进行写操作还会发生异常,带来脏数据的问题,并不可靠,并且更新及删除操作非常困难 (需要整块重写),无法保证数据一致性 (例如 spark 读取时缓存了 parquet 元数据,若元数据变化需要进行 refresh)。​
Delta Lake 不仅能解决上述问题,还能对数据进行各种增强,例如 time travel 等。

运用领域数据湖
诞生起因数据从业者能够利用他们现有的数据湖,同时确保数据质量。
技术栈基于Spark(2.4.2版以后)运用。底层使用Parquet文件格式。存储在HDFS、云存储上。
使用方法Spark-shell、Spark程序
版本对应:
Delta Lake versionApache Spark version
0.7.0及以上3.0.0 以上
0.7.0 以下2.4.2-2.4.x
解决4个数据湖问题:

1、数据湖的读写是不可靠的:因不安全写入导致脏数据。
2、数据湖中的数据质量很低:非结构化数据转储到数据湖中是非常容易的,没有任何验证模式和数据的机制导致数据质量差。没有Schema不能保证数据可用。
3、随着数据的增加,处理性能很差:数据大导致处理数据性能差。
4、数据湖中数据的更新非常困难:需要读写整个分区或表数据修改后写回。

特性:

1、支持ACID事务:
跨多集群的并发写入,也可以同时修改数据集并查看表的一致性快照,这些写入操作将按照串行执行(乐观锁)
在作业执行期间修改了数据,读取时也能看到一致性快照。
2、Schema管理:
自动验证写入数据是否和表结构相同,如果不同多写入列则报错,少写入列则null填充。
手动添加和自动更新Schema
3、可伸缩的元数据处理:元数据信息存储在事务日志中,不是存储在元存储(metastore)中,可固定的时间内列出大型目录中的文件,并且在读取数据时非常高效
4、数据版本:类似于Hbase的版本号。
5、统一的批处理和流 sink:可使用作为 Apache Spark structured streaming 高效的流 sink,结合 ACID 事务和可伸缩的元数据处理,高效的流 sink 现在支持许多接近实时的分析用例,而且无需维护复杂的流和批处理管道
6、数据存储格式采用开源 Apache Parquet
7、支持更新和删除: 支持 merge, update 和 delete 等 DML 命令
8、流批同表: Spark流批操作可共享同一张表。即是流的表,也是批的表。

文件目录结构:

_delta_log:事务日志
part-< taskId>-< jobid>-c< bucketId>.snappy.parquet:实际存储文件(parquet格式+snappy压缩)

文件目录结构

相关机制

更新机制:

delta lake更新数据时会先定位待更新数据所在的文件,使用spark join获得结果数据集,将更新后的数据和文件中其他不需要更新的数据一起写入到新的文件里,同时在commit log中记录AddFile(新文件)和RemoveFile(旧文件)两种action。
更新机制

merge的自动模式:

在merge的时候,有两种模式:默认模式和自动模式。(开启方法:spark.config(“spark.databricks.delta.schema.autoMerge.enabled”,true) )
默认模式下:不会更改Schema,当merge的两张表Schema不相同时,自动抛弃不同的Schema,丢失造成数据。
自动模式:自动更改Schema,当merge的两张表Schema不相同时,自动合并不同的Schema。
测试案例传送门:Dalta Lake测试案例

   df 				df1
+---+---+----+ ||  +---+---+----+---+---+
| id| rn|flag| ||  | id| rn|flag|  A|  B|
+---+---+----+ ||  +---+---+----+---+---+
|  4|  3| 100| ||  |  4|  3| 200|  a|  B|
|  1|  3| 100| ||  |  1|  3| 200|  a|  B|
|  3|  3| 100| ||  |  3|  3| 200|  a|  B|
|  2|  3| 100| ||  |  2|  3| 200|  a|  B|
|  5|  3| 100| ||  +---+---+----+---+---+
+---+---+----+
df.merge(df1)的情况下(merge into df)
     默认模式                    自动模式
+---+---+----+ ||    +---+---+----+-----+-----+
| id| rn|flag| ||    | id| rn|flag|  A  |  B   |
+---+---+----+ ||    +---+---+----+-----+-----+
|  4|  3| 200| ||    |  4|  3| 200|  a  |  B   |
|  1|  3| 200| ||    |  1|  3| 200|  a  |  B   |
|  3|  3| 200| ||    |  3|  3| 200|  a  |  B   |
|  2|  3| 200| ||    |  2|  3| 200|  a  |  B   |
|  5|  3| 100| ||    |  5|  3| 100| null|  null|
+---+---+----+ ||    +---+---+----+-----+-----+
事务日志(_delta_log):

作用:
是跟踪所有用户对表所做的所有更改的中央存储库;
根据事务日志,保证用户看到的表始终是最新版本的;
用户不能对表进行不同的、有冲突的更改;
记录每一次操作成功后表的状态,包括对应的元数据等信息。
位置:建表时自动创建子目录_delta_log下
命名:对表的其他更改将按升序数字顺序生成后续的json文件,从000000.json开始,下一次提交以000001.json的形式写出,依此类推
何时生成:每个操作生成一个事务日志文件,日志文件中记录了所有的有序原子操作
存储内容:每个JSON文件是一次更改,修改一行内容是一行数据。如下是增加2行数据。
在这里插入图片描述

检查点文件:

delta lake每提交10次就会自动生成检查点文件。这些检查点文件在一个时间点上保存了整个表的状态,采用Parque格式。
等于每10次操作一个表全量数据快照。

数据回溯:

每个在事件日志下,每个json文件是一个回溯版本。比如你文件下一共有10个json文件(000000.json~000010.json),那么你就有10个可回溯的版本。
默认情况下,没有在Delta表上运行VACUUM(清除历史),增量表将提交历史记录保留30天。如果运行VACUUM,您将无法恢复到默认的7天之前的数据版本。
查看历史操作方式:dff.history()

+-------+-------------------+------+--------+---------+------------------------------------------+----+--------+---------+-----------+--------------+-------------+----------------------------------------
|version|timestamp          |userId|userName|operation|operationParameters                       |job |notebook|clusterId|readVersion|isolationLevel|isBlindAppend|operationMetrics                                                                   |userMetadata|
+-------+-------------------+------+--------+---------+------------------------------------------+----+--------+---------+-----------+--------------+-------------+-----------------------------------------------------------------------------------+------------+
|7      |2021-01-28 18:06:04|null  |null    |DELETE   |[predicate -> ["(`id` = 2)"]]             |null|null    |null     |6          |null          |false        |[numRemovedFiles -> 1, numDeletedRows -> 1, numAddedFiles -> 1, numCopiedRows -> 3]|null        |
|6      |2021-01-28 18:04:01|null  |null    |WRITE    |[mode -> Overwrite, partitionBy -> []]    |null|null    |null     |5          |null          |false        |[numFiles -> 1, numOutputBytes -> 886, numOutputRows -> 4]                         |null        |
|5      |2021-01-28 18:03:59|null  |null    |UPDATE   |[predicate -> (id#395 > 2)]               |null|null    |null     |4          |null          |false        |[numRemovedFiles -> 1, numAddedFiles -> 1, numUpdatedRows -> 2, numCopiedRows -> 2]|null        |
|4      |2021-01-28 18:03:24|null  |null    |WRITE    |[mode -> Overwrite, partitionBy -> []]    |null|null    |null     |3          |null          |false        |[numFiles -> 1, numOutputBytes -> 886, numOutputRows -> 4]                         |null        |
|3      |2021-01-28 18:03:22|null  |null    |UPDATE   |[predicate -> (id#395 > 2)]               |null|null    |null     |2          |null          |false        |[numRemovedFiles -> 1, numAddedFiles -> 1, numUpdatedRows -> 2, numCopiedRows -> 2]|null        |
|2      |2021-01-28 17:14:39|null  |null    |UPDATE   |[predicate -> (id#395 > 2)]               |null|null    |null     |1          |null          |false        |[numRemovedFiles -> 1, numAddedFiles -> 1, numUpdatedRows -> 2, numCopiedRows -> 2]|null        |
|1      |2021-01-28 16:55:04|null  |null    |WRITE    |[mode -> Overwrite, partitionBy -> []]    |null|null    |null     |0          |null          |false        |[numFiles -> 1, numOutputBytes -> 886, numOutputRows -> 4]                         |null        |
|0      |2021-01-28 16:47:43|null  |null    |WRITE    |[mode -> ErrorIfExists, partitionBy -> []]|null|null    |null     |null       |null          |true         |[numFiles -> 5, numOutputBytes -> 2007, numOutputRows -> 4]                        |null        |
+-------+-------------------+------+--------+---------+------------------------------------------+----+--------+---------+-----------+--------------+-------------+----------------------------------------
项目ValueDescription
versionlong操作生成的表版本。
timestamptimestamp提交此版本时间。
userIdstring运行该操作的用户的ID。
userNamestring运行该操作的用户名。
operationstring操作参数名称。
operationParametersmap操作的动作。
jobstruct运行该操作的作业的详细信息。
notebookstruct运行该操作的详细信息。
clusterIdstring运行操作的群集的ID。
readVersionlong读取以执行写操作的表的版本。
isolationLevelstring用于此操作的隔离级别。
isBlindAppendboolean此操作是否附加数据。
operationMetricsmap操作的指标(例如,行数和已修改的文件)
userMetadatastring用户定义的提交元数据(如果已指定)

总结:

1、底层:使用 parquet 文件格式
2、Delta Lake = Parquet文件 + Meta 文件 + 一组操作的API
3、Delta Lake 其实只是一个Lib库 Delta Lake 是一个lib 而不是一个service,不同于HBase,他不需要单独部署,而是直接依附于Spark计算引擎的。目前只支持Spark引擎。这意味什么呢?Delta Lake 和普通的parquet文件使用方式没有任何差异,你只要在你的Spark代码项目里引入delta包,按标准的Spark datasource操作即可,可谓部署和使用成本极低。

优点缺点
支持事务,支持校验,支持DML过度依赖Spark
数据版本,可追溯数据历史Delta Lake不支持多表事务和外键
小文件合并Delta不支持DStream API
流批同表没有权限和用户管理

个人理解:

Delta Lake 是一个开源的存储层,为数据湖带来了可靠性。
Delta Lake可以做到流式数据的DML操作,实时的展示操作后的数据信息。
也可以做到流批同表的操作,对于操作简易很多。
Delta Lake 太过依赖Spark,目前仅支持Spark。
还有个痛点,因为涉及到数据存储层,其中的血缘问题也无法解决。
没有兼容atlas,希望后面版本可以支持。


数据湖 Delta对比Iceberg(对比时间2021-04)

类型对比点Delta Lake 0.7.0Iceberg 0.11.0
存储存储支持HDFS, S3 (Databricks), OSSHDFS, S3 (Databricks)
-压缩支持parquetavro,parquet
-历史回滚支持支持
-快照支持支持
-支持引擎SparkSpark、Flink
Hive建表Hive建表其他引擎无法操作。支持建立映射表Hive建表其他引擎无法操作。支持建立映射表
-查询不能直接查询Spark表,需要映射不能查询Spark\Flink的iceberg表,需要映射
-insert/delete/update/merge不支持不支持
-alter table不支持不支持
-主键不支持不支持
Spark3.0-SQL链接方式catalogcatalog(操作时需切换namespace)
-建/删表支持支持
-查询支持支持
-alter table支持支持
注意:改数据类有限制(只允许加宽类型int -> bigint。类型不可以随意转换int 不可转string)
-历史查看/回滚/删除支持查看/删除支持(需要调用自带的CALL,测试暂未通过)
-建立快照支持(操作10次自动快照),但不支持手动创建快照支持(需要调用自带的CALL,测试暂未通过),并可手动设置快照,自定义快照名称
-与其他引擎兼容只允许Hive外表查看,不允许更改只允许Hive外表查看,不允许更改
允许Flink查看及操作
-其他表运维操作少于iceberg。
Delta不支持:删除表未引用文件、使用快照建立临时表、其他表迁移至ice表等
不允许show create table
show tables 只展示ice表
Spark3.0-Streamingsource支持不支持
-sink支持(无主键)支持(无主键)
-merge sink不支持不支持
Flink1.11.0-SQL链接方式不支持Flinkcatalog
-建/删表不支持Flink支持
-查询不支持Flink支持
-insert/delete/update/merge不支持Flink仅支持insert into
-alter table不支持Flink仅支持改表名
如果需要更改表结构/类型等操作,需切换Spark引擎操作
-与其他引擎兼容不支持Flink只允许Hive外表查看,不允许更改,允许Spark查看及操作
Flink1.11.0-Streamingsource不支持Flink不支持
-sink不支持Flink支持(无主键)
-merge sink不支持Flink不支持

测试案例传送门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值