过往记忆大数据 过往记忆大数据
Delta Lake 是今年数砖在 Spark+AI Summit 2019 会议上开源的项目,详见【重磅 | Apache Spark 社区期待的 Delta Lake 开源了】,当时文章只是简单介绍了下功能,本文将深刻介绍 Apache Spark Delta Lake 的事务日志,经过本文咱们能够了解 Delta Lake 的 ACID 是如何实现的,这些巧妙的设计很是值得咱们学习。
事务日志是理解 Delta Lake 的关键,由于它是贯穿许多最重要功能的通用模块,包括 ACID 事务、可扩展的元数据处理、时间旅行(time travel)等。本文咱们将探讨事务日志(Transaction Log)是什么,它在文件级别是如何工做的,以及它如何为多个并发读取和写入问题提供优雅的解决方案。html
Delta Lake 事务日志(也称为 DeltaLog)是 Delta Lake 表上执行每次事务的有序记录。具体形式以下:数据库
单一事实来源json
Delta Lake 构建于 Apache Spark™ 之上,容许多个写和读操做同时对给定表进行操做。为了始终向用户显示正确的数据视图,事务日志可做为单一事实来源(single source of truth) - 中央存储库,用于跟踪用户对表所作的全部更改。缓存
当用户第一次读取 Delta Lake 表或在打开的表上运行一个新查询,该表自上次读取以来已被修改,Spark 会检查事务日志来查看已向表写入的新事务,而后使用这些新更改更新最终用户的表。这可确保用户表的版本始终与最新查询中的主记录同步,而且用户没法对表进行不一样的,冲突的更改。微信
原子性是 ACID 事务的四个属性之一,它能够保证在 Delta Lake 上执行的操做(如 INSERT 或 UPDATE )要么所有成功要么所有不成功。若是没有此属性,硬件故障或软件错误很容易致使数据仅部分写入表中,从而致使数据混乱或损坏。并发
事务日志是 Delta Lake 可以提供原子性保证的机制。不管如何,若是它没有记录在事务日志中,它就不会发生。经过只记录彻底执行的事务,并使用该记录做为惟一的真相来源,事务日志容许用户对其数据进行推理;而且即便数据在 PB 级别上,咱们也能够对这些数据的准确性高枕无忧。ide
将事务分解为原子提交
每当用户执行修改表的操做(例如插入、更新或删除)时,Delta Lake 将该操做分解为一系列由如下一个或多个操做组成的离散步骤:oop
Commit info:包含有关提交的信息以及该操做是在什么时候何地进行的。
而后这些操做将按照有序的原子单位记录在事务日志中,称为提交。
例如,假设用户建立一个事务以向表中添加新列,并向其中添加更多数据。Delta Lake 会将该事务分解为多个部分,一旦事务完成,就将它们添加到事务日志中,以下所示:学习
若是想及时了解Spark、Hadoop或者HBase相关的文章,欢迎关注微信公众号:iteblog_hadoop
所以,若是咱们经过从数据文件 1.parquet 和 2.parquet 向表中添加记录。该事务将自动添加到事务日志中,并以 000000.json 的形式保存到磁盘。而后,咱们改变主意并决定删除这些文件并添加一个新文件(3.parquet)。这些操做将记录为事务日志中的下一个提交,也就是 000001.json,以下所示。大数据
若是想及时了解Spark、Hadoop或者HBase相关的文章,欢迎关注微信公众号:iteblog_hadoop
尽管 1.parquet 和 2.parquet 再也不是咱们 Delta Lake 表的一部分,但它们的添加和删除仍记录在事务日志中,由于这些操做是在咱们的表上执行的 - 尽管它们最终相互抵消了。 Delta Lake 仍然保留这样的原子提交,以确保在须要审计表或使用“时间旅行”来查看表在给定时间点的样子时,咱们可以准确地作到这一点。
此外,即便咱们从表中删除了基础数据文件,Spark 也不会马上从磁盘中删除文件。用户可使用 VACUUM 命令删除再也不须要的文件。
一旦咱们提交了10次事务日志,Delta Lake 就会在相同的 _delta_log 子目录中以 Parquet 格式保存一个检查点文件(如上面的 00000000000000000010.checkpoint.parquet 文件)。每 10 次提交 Delta Lake 会自动生成检查点文件,这个是经过参数 checkpointInterval 参数设置。使用检查点文件(Checkpoint Files)快速从新计算状态
若是想及时了解Spark、Hadoop或者HBase相关的文章,欢迎关注微信公众号:iteblog_hadoop
这些检查点文件在某个时间点保存表的整个状态 - 以原生的 Parquet 格式保存,Spark 能够快速轻松地读取。换句话说,它们为 Spark reader 提供了一种“快捷方式”来彻底复制表的状态,从而容许 Spark 避免从新处理可能存在的数千个低效的小 JSON 文件。
为了提升速度,Spark能够运行一个 listFrom 操做来查看事务日志中的全部文件,快速跳转到最新的检查点文件,而且只处理自保存了最新的检查点文件以来提交的JSON。
为了演示这是如何工做的,假设咱们已经建立了提交,而且事务日志已经记录到 000007.json。Spark 加快了提交的速度,并在内存中自动缓存了表的最新版本。与此同时,其余一些写入者(多是您过于热心的队友)已经向表中写入了新数据,并事务日志已经记录到 0000012.json 了。
为了合并这些新事务并更新表的状态,Spark 将运行 listFrom 方法来查看版本7以后对表的新更改。
若是想及时了解Spark、Hadoop或者HBase相关的文章,欢迎关注微信公众号:iteblog_hadoop
Spark能够直接跳到最近的检查点文件(上图中的 0000010.checkpoint.parquet 文件),而不须要处理全部中间 JSON 文件,由于这个检查点文件包含 commit #10 中表的整个状态。如今,Spark 只需执行 0000011.json 和 0000012.json 的增量处理便可得到表的当前状态。而后 Spark 将表的版本12的状态缓存到内存中。经过遵循此工做流程,Delta Lake 可以使用 Spark 以高效的方式始终更新表的状态。
如今咱们已经在高层次上了解了事务日志如何工做的,让咱们来谈谈并发性。到目前为止,咱们的示例主要涵盖了用户线性提交事务或至少没有冲突的状况。可是当 Delta Lake 处理多个并发读写时会发生什么?
答案很简单,因为 Delta Lake 由 Apache Spark 提供支持,所以不只可让多个用户同时修改表 - 这是预期的。为了处理这些状况,Delta Lake 采用了乐观的并发控制。
乐观并发控制是一种处理并发事务的方法,它假定不一样用户对表所作的事务(更改)能够在不相互冲突的状况下完成。它的速度快得使人难以置信,由于当处理 PB 级的数据时,用户极可能同时处理数据的不一样部分,从而容许他们同时完成不冲突的事务。
例如,假设你和我正在一块儿玩拼图游戏。只要咱们都在作拼图的不一样部分——好比你在角落里,我在边缘上——咱们没有理由不能同时作更大拼图的那一部分,而且以两倍的速度完成拼图。只有当咱们同时须要相同的部件时,才会产生冲突。这就是乐观并发控制。
相反,一些数据库系统使用悲观锁定的概念,这是假设最坏的状况——即便咱们有10,000块拼图,在某个时候咱们确定须要相同的拼图——这致使了太多的冲突。为了解决这个问题,它的理由是,应该只容许一我的同时作拼图,并把其余人都锁在房间外面。这不是一个快速(或友好)解决难题的方法!
固然,即便使用乐观并发控制,有时用户也会尝试同时修改数据的相同部分。幸运的是,Delta Lake 有相应的协议处理它。
为了提供ACID事务,Delta Lake 有一个协议,用于肯定提交应该如何排序(在数据库中称为 serializability),并肯定在同时执行两个或多个提交时应该作什么。Delta Lake经过实现互斥(mutual exclusion)规则来处理这些状况,而后尝试乐观地解决任何冲突。该协议容许Delta Lake遵循ACID隔离原则,该原则确保多个并发写操做以后的表的结果状态与那些连续发生的写操做相同,而且是彼此隔离的。
通常来讲,这个过程是这样进行的
若是想及时了解Spark、Hadoop或者HBase相关的文章,欢迎关注微信公众号:iteblog_hadoop
每一个表都是事务日志中记录的全部提交的总和的结果—很少也很多。事务日志提供了一步一步的指导,详细描述了如何从表的原始状态转换到当前状态。
所以,咱们能够经过从原始表开始从新建立表在任什么时候间点的状态,而且只处理在该点以前提交的数据。这种强大的功能被称为“时间旅行”,或数据版本控制,在任何状况下都是救星。有关更多信息,请参考介绍大型数据湖的 Delta 时间旅行。
做为对 Delta Lake 表所作的每一个更改的最终记录,事务日志为用户提供了可验证的数据血统,这对于治理、审计和合规性目的很是有用。它还能够用于跟踪一个意外更改或管道中的一个 bug 的起源,以追溯到致使该更改的确切操做。用户能够运行 DESCRIBE HISTORY 来查看所作更改的元数据。
在本博客中,咱们深刻研究 Delta Lake 事务日志的工做原理。咱们讨论了: