11.数据湖之deltalake深入学习delta lake的事务日志
理解delta lake的事务日志很有必要,因为其是贯穿很多特性的主线,比如ACID事务,可伸缩的元数据处理,时间旅行等特性。本文详细探讨什么是delta lake的事务日志,它在文件级别的工作方式,以及如何为并发读写问题提供完美的解决方案。
1. 什么是delta lake事务日志
delta lake事务日志,也叫做deltalog,是自delta lake表创建依赖,已经执行过的每个事务的有序记录。
2. 事务日志的作用
2.1 事务日志是真理的单一来源
Delta Lake建立在Apache Spark™之上,以便实现多reader和writer同时读写delta lake表。Delta Lake事务日志 始终向用户展示正确的数据视图–记录用户对表所做的所有更改。
当用户第一次读取delta lake的表时或者在修改过的表上执行新的查询的时候,spark会检查事务日志,查看是否对该表有新的事务操作,假设有的话会用这些新的变更区更新用户的目标表。这样可以确保用户的表版本始终与最近一次查询的主记录保持同步,并且确保用户不能对表进行不同的冲突更改。
2.2 deltalake原子操作实现
ACID事务的四个属性之一,atomicity,确保对数据湖执行的操作(例如INSERT或UPDATE)要么完全完成,要么完全终止。没有此属性,硬件故障或软件错误很容易导致数据仅部分写入表中,从而导致数据混乱或损坏。
事务日志是Delta Lake能够提供原子性保证的机制。出于所有目的和目标,如果未将变更记录在事务日志中,则变更永远不会生效。通过仅记录完整和完全事务行为,并将该记录用作真相的唯一来源,Delta Lake事务日志使用户能够处理pb级的数据,且对信任其处理过程的可靠性。
3.事务日志工作机制
3.1 将事务日志拆分为原子提交
用户对delta lake表的修改操作,如insert,update或者delete操作,delta lake会将该操作划分为一系列具体的操作步骤(一个或者多个)。
a).add file 增加一个数据文件
b).remove file 删除一个数据文件
c). update metadata 更新表的元数据,比如修改表的名称,schema 或者分区。
d).set transaction 使用给定的ID记录structured streaming job 提交的微批。
c).change protocol 就是切换delta lake事务日志为最新的软协议,使其新的特性生效。
e).commit info 提交变更信息。
然后将这些操作作为有序的原子单元记录在事务日志中。
例如,假设用户创建一个事务以向表中添加新列,并向其中添加更多数据。Delta Lake会将事务分解为各个组成部分,一旦事务完成,请按以下cmmit将其添加到事务日志中:
- 更新元数据–修改schema,以包含新列
- 添加文件
4. 文件级别的事务日志
当用户创建delta lake 表的时候,该表的事务日志也会自动创建,目录是在_delta_log的子目录。当用户对表做变更的时候,这些变更会以有序和原子提交的形式记录到事务日志中。每次提交都是写为一个json文件,以000000.json开头。对表的后续操作文件名称会依次记录为,000001.json,000002.json文件等等,如下:
因此,作为示例,我们可以从1.parquet和2.parquet文件取数据,添加到delta lake表中。该事务将自动添加到事务日志中,并作为commit文件000000.json保存到磁盘。接着我们也可以决定删除这些文件并添加一个新文件(3.parquet)。这些操作将被记录为事务日志中的下一次cmmit文件000001.json,如下所示。
尽管1.parquet和2.parquet不再是delta 表的一部分,但是它们的添加和删除操作仍然记录在事务日志,毕竟是delta lake表变更的一部分。Delta Lake仍保留此类原子提交,以确保在需要审计表或使用“时间旅行”查看表在给定时间点表的情况时,可以准确地做到这一点。
同样,即使我们从表中删除了数据文件,Spark也不立即从磁盘中删除文件。用户可以使用VACUUM删除不再需要的文件。
5.使用checkpoint快速重新计算状态
一旦我们的事务日志提交了十次以后,会生成十个json文件,delta lake此时会进行一次checkpoint,也即是在_delta_log目录下生成一个parquet格式的checkpoint文件。delta lake会自动的每10个commit 生成一次checkpoint文件。
这些checkpoint文件会在某个时间点保存表的整个状态-以本机Parquet格式保存,Spark可以轻松快速地读取它们。换句话说,checkpoint文件为Spark的reader提供了一种“捷径”,可以完全重现表的状态,从而使Spark可以避免重新处理可能是成千上万个微小,效率低下的JSON文件。
为了加快速度,Spark可以运行listFrom操作以查看事务日志中的所有文件,快速跳至最新的checkpoint文件,并且仅处理自保存最新checkpoint文件以来进行的那些JSON commits。
为了演示它是如何工作的,假设,我们commit文件创建到了000007.json,如下图所示。Spark已在内存中自动缓存了表的最新版本,从而加快了提交的速度。但是,与此同时,其他几个writer(也许是需要的数据变更commit)已经向表中写入了新数据,并一直添加commit到了0000012.json。
为了合并这些新事务并更新表的状态,Spark然后将运行listFrom version 7操作以查看表的新更改。
无需处理所有中间JSON文件,Spark可以跳到最新的checkpoint文件,因为它包含commit#10时表的整个状态。现在,Spark只需执行表的增量处理0000011.json并0000012.json。然后,Spark将表的版本12缓存在内存中。通过遵循该工作流程,Delta Lake可以使用Spark随时高效地保存表状态的更新。
6.处理多并发读写
经过前面的介绍,应该大致了解在最上层delta lake事务日志的工作逻辑,接下来讨论并发处理。前面主要介绍了线性或者至少没有冲突的事务 行为。但是当delta lake处理多并发读写的时候会发生什么呢?
答案很简单。为了实现并发控制delta lake引入了乐观并发控制。
6.1 什么是乐观并发控制
乐观并发控制是一种处理并发事务的方法,该方法假定不同用户对表进行的事务(更改)可以完成而不会相互冲突。该方法之所以如此之快,是因为在处理PB级数据时,不同用户极有可能会处理数据的不同部分,从而使他们能够同时无冲突的完成事务。
例如,假设A和B正在一起研究拼图游戏。只要我们都在其中的不同部分上工作(例如,A在上半部分,而B在下班部分),接可以实现AB分别完成一部分拼图,并以两倍快的速度完成拼图。只有当我们需要相同的零件时,才会有冲突。那就是乐观的并发控制。
当然,即使采用了乐观的并发控制,有时用户的确会尝试同时修改数据的相同部分。幸运的是,Delta Lake对此有一个协议。
6.2 乐观的解决冲突
为了提供ACID 事务,Delta Lake有一个协议来确定commit的顺序(在数据库中称为可序列化性的概念),并确定在同时进行两个或多个commit的情况下该怎么做。Delta Lake通过实施互斥规则处理这些案例,然后尝试乐观地解决任何冲突。此协议允许Delta Lake遵循ACID 隔离原则,该原则可确保在多次并发写入后,表的最终状态相同。
通常,该过程如下进行:
- 记录表的起始版本。
- 记录读/写。
- 尝试提交。
- 如果有人获胜,请检查已读的内容是否已更改。
- 重复上述过程。
要了解所有这些信息是如何实时进行的,让我们看一下下面的图,看看Delta Lake在发生冲突时如何处理冲突。想象一下,有两个用户从同一个表中读取数据,然后每个用户都尝试向其中添加一些数据。
- Delta Lake记录在进行任何变更之前读取的表(版本0)的起始表版本。
- 用户1和2都尝试同时向表中添加一些数据。在这里,我们陷入了冲突,因为下一次只能提交一次并记录为000001.json。
- Delta Lake通过“互斥”的概念解决了这一冲突,这意味着只有一个用户可以成功进行提交000001.json。用户1的提交被接受,而用户2的提交被拒绝。
- Delta Lake宁愿乐观地处理此冲突,也不愿为User 2引发错误。它检查是否对表进行了任何新的提交,并以静默方式更新表以响应这些更改,然后简单地在新提交的表上重试用户2的提交(不进行任何数据处理),并成功提交000002.json。
在大多数情况下,这种冲突解决是无声,无缝且成功地进行的。但是,如果存在无法解决的问题,Delta Lake无法乐观地解决(例如,如果用户1删除了用户2也删除的文件),则此时唯一的选择就是抛出错误。
最后一点,由于在Delta Lake表上进行的所有事务都直接存储到磁盘,因此此过程满足了ACID属性的持久性,这意味着即使系统发生故障,该操作的效果也将持续存在。
7.其他的使用案例
7.1 时间旅行
每个表都是Delta Lake事务日志中记录的所有commit的总和的结果。事务日志提供了变更过程的记录,详细说明了如何从表的原始状态变为当前状态。
因此,我们可以通过从原始表开始在任何时间点重新创建表的状态,并且仅处理该点之前的commit。这种强大的功能被称为“时间旅行”或数据版本控制。
7.2 数据血缘和debug
作为对表进行的每次更改的明确记录,Delta Lake事务日志为用户提供了可验证的数据血缘,可用于治理,审计和合规性目的。它也可以用来追溯意外变更的源头或pipeline过程中的错误,以追溯到引起该错误的确切操作。用户可以运行DESCRIBE HISTORY来查看有关所做更改的元数据。