数据工程师的原子性
数据工程师的原子性
原子性 ,在程序或系统中,是将多个操作的执行视为单个操作执行的概念。
组合是构建软件系统的主要概念,即服务通常由其他组件服务构建,功能通常由其他功能组成。现在有时你需要一个操作,它由其他子操作组成,作为一个单元成功或失败。假设你有一个函数, func_a
和 func_a
来电 func_b
和 func_c
在其定义中,每个都可以独立失败。如果 func_a
是原子的,然后调用 一个
是一次成功的手术 当且仅当 每个组件函数调用都执行没有任何错误,即 func_b
和 func_c
都成功执行。同样,如果任何组件函数调用抛出异常(返回错误或为我的 去 朋友)。
让我们看看为什么原子性是数据工程中的核心概念,并在您可能需要它时看看现实生活场景。
为什么需要原子性?
使操作原子化的主要原因是防止数据不一致和损坏状态。为了解释这个术语——破坏状态——我将使用最流行的例子来说明缺乏原子性是如何导致它的。
假设你正在开发一个银行应用程序,并且你有一个带有这个签名的 transfer_funds 函数—— func transferFunds(从int,到int,金额浮动)
.可以合理地假设该函数实现的核心将包括两个子操作,即借记 数量
来自 从
帐户和贷记 数量
到 至
帐户。现在想象在给定的调用中 转移资金
,操作1(借方 从
帐户)失败,但操作 2(信用 至
帐户)成功。我们最终会归功于 至
从未从 从
账号。如果发生相反的情况,则结果类似——操作 1 成功,操作 2 失败。这就是破碎状态的样子,在这个例子中,它会产生现实世界的后果![1]
可逆性
请注意,如果其中一个组件函数抛出异常,就说资金转移操作失败是不够的。想象一下第一个操作成功但第二个操作失败。在调用程序中,我们可以捕捉到这个异常,并向用户返回回传操作失败的错误信息。但这既幼稚又不正确!用户将立即看到他们丢失了 数量
来自 从
没有看到相应的信用 至
帐户。这就引出了理解原子性的最重要概念——组件操作必须是可逆的!如果一个组件操作不能被逆转,那么我们不能强制整个操作作为一个单元成功或失败。
保持这个想法 ** 可逆性** 请记住,您应该很快意识到并非所有操作都可以是原子的,因为某些子操作是不可逆的。例如,如果操作的一个子操作涉及发送电子邮件,则该操作不能成为原子操作,因为您无法收回已发送的电子邮件。 [2]除非我们在这种情况下通过发送后续电子邮件来提醒接收者忽略之前的电子邮件来实现可逆性。
在数据库或数据仓库中实现原子性
交易
请注意,原子性的概念几乎存在于每个软件领域,例如数据库、网络、文件系统、编程语言、操作系统等,但我将这里的示例限制在数据库/数据仓库领域,因为这是我最熟悉的领域和。
在现代数据库或数据仓库中,数据工程师工具箱中用于实现原子性的最重要工具是 交易 .事务是一个逻辑组件,用于封装所有成功或失败的 sql 语句作为一个单元(请注意,数据库事务提供的不仅仅是原子性,请参阅 酸 更多细节)。在 PostgreSQL 例如,我们可以通过执行以下任何关键字来发起交易—— 开始
, 开始交易
, 开始工作
.事务启动后,我们可以将所有组件操作定义为事务中的单个 SQL 语句。随着每条语句的执行,我们可以监控单个查询失败。如果任何查询失败,我们可以通过执行 回滚
命令。如果它们都成功了,我们可以通过执行 犯罪
命令。
固有的原子语句
除了显式使用事务之外,另一种实施原子性的方法是利用某些数据库或数据仓库操作提供的固有原子性。一些数据库/数据仓库命令在其内部实现中使用原子性,因此无需在事务中执行两个单独的命令,可以简单地执行这些固有的原子单个命令。我用 Snowflake 做了很多工作,所以让我们看一个例子。
我们的任务是用来自不同表的新数据和更新数据替换表的全部内容。我们可以通过多种方式定义此操作。一种方法是运行 2 SQL DDL 命令——首先删除表( 删除表
),然后用新数据重新创建表( 创建表…作为选择…
)。现在, Snowflake 在立即提交的隐式事务中执行每个 DDL 命令 .这意味着我们不能将这两个命令都包装在显式事务中,并且 回滚
如果其中一个失败。因此,如果 drop table 命令成功,但随后的 create table 命令失败,我们将没有数据并且可能搞砸了!
解决这个问题的一个简单方法是将这个操作重新定义为一个序列 截断表
和 插入
桌子 DML 命令。在 Snowflake 中,您可以在显式事务中包装和执行 DML 命令,因此我们可以使用此选项。
利用某些数据库/数据仓库操作提供的固有原子性的更好的选择是 交换表命令 .在这里,我们只需运行此命令 ALTER TABLE old_data_table 与 new_data_table 交换
. Snowflake 保证此操作将作为一个逻辑单元执行,因此我们可以确信我们不会因为一个命令失败而另一个命令成功而导致没有数据或有重复数据的损坏状态。
结论
原子性是每个编写状态改变程序的工程师(字面意思是每个人)都应该非常熟悉的核心概念。当使用在单个操作中修改两个或多个对象的状态的程序时更是如此。这种类型的一些示例包括对象重命名、资金转移、暂存到生产部署、交换对象、恢复更改等。
在处理这类问题时,您应该始终问这些问题:
- 这个操作应该是原子的吗?
- 我可以使用我的编程语言、数据库、文件系统、操作系统等中的哪些工具来实现原子性?
脚注
[1] 这不是应用程序最终处于损坏状态的唯一方式。看另一个 酸 属性以获取更多详细信息。
[2] 并非所有电子邮件服务都具有“未发送”功能
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明