事务是一系列被视为“单一工作单元”的数据库操作。事务中的操作要么全部成功,要么全部失败。通过这种方式,事务的概念支持系统部分故障时的数据完整性。并非所有数据库都选择支持 ACID 事务,有时它们会优先考虑其他优化,这些优化很难或理论上不可能一起实现。
通常,关系数据库支持ACID事务,而非关系数据库不支持(也有例外)。
状态
数据库中的事务可以处于以下状态之一:
活跃状态(Active)
在此状态下,事务正在执行。这是每个事务的初始状态。
部分提交状态(Partially Committed)
当事务执行其最终操作时,它处于部分提交状态。
提交状态(Committed)
如果事务成功执行了所有操作,则称为提交。它的所有效果现在都永久保存在数据库系统中。
失败状态(Failed)
如果数据库恢复系统进行的任何检查失败,则事务将处于失败状态。失败的事务无法继续。
中止状态(Aborted)
如果有任何检查失败且事务已达到失败状态,则恢复管理器将回滚对数据库的所有写入操作,以使数据库恢复到执行事务之前的原始状态。事务此时的状态为中止。
数据库恢复模块可以在事务中止后选择以下两种操作之一:
-
重新启动事务
-
终止交易
结束状态(Terminated)
如果没有任何回滚或来自提交状态的事务,则系统当前一致并准备好处理新事务,旧事务结束。
分布式事务
分布式事务是跨两个或多个数据库对数据执行的一组操作。它通常在由网络连接的独立节点之间进行协调,但也可以跨单个服务器上的多个数据库。
为什么我们需要分布式事务?
与单个数据库上的 ACID 事务不同,分布式事务涉及更改多个数据库上的数据。分布式事务处理更加复杂,因为数据库必须作为一个独立单元协调事务中更改的提交或回滚。
换句话说,所有节点都必须提交,否则所有节点都将中止,整个事务将回滚。这就是为什么我们需要分布式事务。
现在,让我们看看一些流行的分布式事务解决方案:
两阶段提交
两阶段提交(2PC)协议是一种分布式算法,它协调参与分布式事务的所有进程,确定是否提交或中止(回滚)事务。
该协议在许多临时系统故障的场景下也能实现其目标,因此被广泛使用。然而,它并非对所有可能的故障配置都具有弹性,在极少数情况下,需要手动干预来改正结果。
该协议需要一个协调器来协调和监督不同节点之间的事务。协调器试图在两个阶段的一组进程中建立共识并因此得名。
阶段
两阶段提交包括以下阶段:
准备阶段
准备阶段涉及协调节点从每个参与者节点收集共识。事务在每个节点都响应准备就绪之前一直处于中止状态。
提交阶段
如果所有参与者都向协调器响应他们准备好了,那么协调器会要求所有节点提交事务。如果发生故障,事务将回滚。
问题
在两阶段提交协议中可能存在以下问题:
- 如果其中一个节点崩溃怎么办?
- 如果协调器本身崩溃怎么办?
- 它是一个阻塞协议。
三阶段提交
三阶段提交(3PC)是两阶段提交的扩展,其中提交阶段分为两个阶段。这有助于解决两阶段提交协议中出现的阻塞问题。
阶段
三阶段提交包括以下阶段:
准备阶段
此阶段与两阶段提交相同。
提交前阶段
协调器发出预提交消息,所有参与者节点必须确认该消息。如果参与者未能及时收到此消息,则事务将中止。
提交阶段
此步骤也类似于两阶段提交协议。
什么提交前阶段有帮助?
预提交阶段完成以下工作:
-
如果在此阶段中找到了参与者节点,则意味着每个参与者都完成了第一阶段。保证准备阶段的完成。
-
每个阶段现在都可以超时,避免无限期等待。
Sagas
Saga 是一系列本地事务。每个本地事务更新数据库并发布消息或事件以触发 Saga 中的下一个本地事务。如果本地事务因违反业务规则而失败,则 Saga 将执行一系列补偿事务,以撤销先前本地事务所做的更改。
协作
有两种常见的实现方法:
- 基于事件的方式(Choreography):每个本地事务发布触发其他服务中本地事务的域事件。
- 基于命令的方式(Orchestration):由协调中心告诉参与者要执行哪些本地事务。
问题
- Saga模式特别难以调试。
- Saga参与者之间存在循环依赖的风险。
- 缺少参与者数据隔离带来了持久性挑战。
- 测试很困难,因为必须运行所有服务才能模拟事务。