目录
掌握Java分布式事务:2PC、3PC、TCC与Seata全解析
摘要:
在现代互联网应用中,分布式事务是确保数据一致性的关键技术。本文深入探讨了分布式事务的核心技术,包括两阶段提交(2PC)、三阶段提交(3PC)、TCC(Try-Confirm-Cancel)以及Seata。读者将能够理解这些技术的原理、优缺点以及在实际项目中的应用。
关键词:
分布式事务,2PC,3PC,TCC,Seata,AT模式,Saga模式,Java,微服务,数据一致性
一、分布式事务的由来
随着互联网技术的发展与数据体量的扩增,软件系统逐渐由单体应用演变为分布式系统/微服务应用。
分布式系统把一个单体应用系统拆分成可独立部署的多个微服务,很多场景下需要服务之间远程协作才能完成事务操作。
这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务。
分布式系统中实现事务,其实是由多个本地事务组合而成。对于分布式事务而言几乎满足不了 ACID。
二、两阶段提交2PC
在两阶段提交(2PC)中,参与者在准备阶段开始执行事务操作,但不会立即提交事务。两阶段提交的详细步骤如下:
1. 准备阶段(投票阶段)
- 事务协调者向所有参与者发送准备请求(prepare请求),询问它们是否准备好提交自己的事务部分。
- 参与者收到准备请求后,会执行所有必要的事务操作,例如更新数据、锁定资源,但不会提交事务。
- 参与者将操作结果写入到持久性日志中,以便在系统崩溃时可以恢复状态。
- 然后,参与者向协调者回复一个投票结果,通常是“同意”或“拒绝”。如果参与者无法提交事务,比如违反了某些业务规则或资源锁定失败,则投票为“拒绝”。
2. 提交阶段
- 协调者根据所有参与者的投票结果做出决策。如果所有参与者都投票“同意”,则进入提交事务;如果任何一个参与者投票“拒绝”,则协调者决定回滚事务。
- 协调者向所有参与者发送提交(commit)或回滚(rollback)的指令。
- 参与者根据协调者的指令执行最终的提交或回滚操作,并释放所有锁定的资源。
3.优缺点
这种机制确保了所有参与者在最终提交之前都已经准备好了,从而维护了事务的原子性和一致性。
然而,这也意味着在准备阶段,参与者需要锁定涉及的所有资源,这可能导致资源的长时间锁定,从而影响系统并发性能。如果协调者在提交阶段失败,且没有适当的恢复机制,可能会导致某些参与者处于不确定状态,这是两阶段提交的一个缺点。
优点:
原子性:确保所有参与者要么全部提交,要么全部回滚,满足ACID属性中的原子性。
原因:集中式决策提高了事务的一致性。
例子:银行转账,两个账户的扣款和存款必须同时成功或失败。
缺点:
性能问题:所有参与者在准备阶段需要等待协调者的指令,造成延迟。
单点故障:协调者故障可能导致整个事务失败。
原因:协调者是单点,对系统稳定性要求高。
例子:如果协调者在提交阶段宕机,所有参与者可能无法及时得知提交或回滚的指令。"
三、三阶段提交3PC
三阶段提交3PC 在2PC的基础上增加了一个预提交阶段,以解决协调者故障时参与者的阻塞问题。
三阶段提交协议(3PC,Three-Phase Commit Protocol)是一种用于确保分布式系统中所有节点对某个操作达成一致的协议。它主要用于数据库事务管理,尤其是在分布式数据库系统中。三阶段提交协议分为以下三个阶段
1. 准备阶段(Prepare Phase)
在这个阶段,协调者(Coordinator)向所有参与者(Participants)发送准备请求(prepare message),询问它们是否准备好提交事务。参与者收到请求后,会执行所有事务操作,但不会提交,而是进入一个预提交状态。如果参与者能够提交事务,则向协调者回复一个肯定的准备响应(prepared response),表示它已经准备好提交事务。如果参与者无法提交事务,则发送否定的准备响应。
2. 预提交阶段(Pre-Commit Phase)
一旦协调者从所有参与者那里收到肯定的准备响应,它将进入预提交阶段。在这个阶段,协调者向所有参与者发送预提交请求(pre-commit message),指示它们提交事务。如果协调者收到任何否定的准备响应,它将进入中断阶段(abort phase)。
参与者在收到预提交请求后,会执行实际的提交操作,并将事务结果写入持久存储。然后,参与者向协调者发送一个预提交确认(acknowledgment),表示事务已经提交。
3. 提交阶段(Commit Phase)
在这个阶段,协调者等待所有参与者的预提交确认。一旦收到所有确认,协调者将向所有参与者发送提交请求(commit message),正式通知它们提交事务。如果协调者在预提交阶段收到否定的预提交响应,或者在提交阶段等待超时,它将进入中断阶段。
参与者在收到提交请求后,会将事务的最终状态写入持久存储,并释放所有事务资源。
4. 中断阶段(Abort Phase)
如果协调者在任何阶段收到否定的响应,或者在等待响应时超时,它将进入中断阶段。在这个阶段,协调者向所有参与者发送中断请求(abort message),指示它们回滚事务。参与者在收到中断请求后,将回滚所有事务操作,并释放事务资源。
三阶段提交协议通过这些阶段确保了分布式系统中的事务一致性,即使在网络分区或其他故障情况下也能保持数据的完整性。然而,它也引入了额外的复杂性和通信开销。
5.举例说明三阶段提交的每个阶段,参与者具体做了啥
让我们通过一个简单的SQL事务来说明三阶段提交协议中参与者的行为假设我们有一个分布式数据库系统,其中有两个节点,A和B。我们想要执行一个事务,该事务涉及到在两个节点上同时更新数据。
事务内容
- 在节点A上,将账户1的余额减少100元。
- 在节点B上,将账户2的余额增加100元。
准备阶段(Prepare Phase)
- 协调者向参与者A和B发送准备请求。
- 参与者A检查账户1的余额是否足够,锁定账户1,准备扣除100元,但不实际进行扣款,记录这个操作到事务日志中。
- 参与者B检查账户2是否可以增加100元,锁定账户2,准备增加100元,但不实际进行增加,记录这个操作到事务日志中。
- 参与者A和B向协调者发送肯定的准备响应,表示它们已经准备好提交这部分事务。
预提交阶段(Pre-Commit Phase)
- 协调者收到所有参与者的肯定准备响应后,向参与者A和B发送预提交请求。
- 参与者A将账户1的扣款操作实际执行,但仍然保持锁定状态,确保操作的原子性。
- 参与者B将账户2的加款操作实际执行,同样保持锁定状态。
- 参与者A和B向协调者发送预提交确认,表示它们已经执行了事务操作,等待最终提交。
提交阶段(Commit Phase)
- 协调者收到所有参与者的预提交确认后,向参与者A和B发送提交请求。
- 参与者A接收到提交请求,正式提交事务,释放对账户1的锁定,确保其他操作可以访问账户1。
- 参与者B接收到提交请求,正式提交事务,释放对账户2的锁定。
- 参与者A和B完成事务的所有后续工作,如清理事务日志,更新数据库状态等。
中断阶段(Abort Phase)
如果在任何阶段,协调者收到否定的响应,或者在等待响应时超时,协调者将进入中断阶段,向所有参与者发送中断请求,参与者将回滚事务,释放所有资源。
通过这个示例,我们可以看到:
- 在准备阶段,参与者并没有实际修改数据,只是做了修改的准备。
- 在预提交阶段,参与者实际执行了修改操作,但仍然保持锁定状态。
- 在提交阶段,参与者完成了事务的最终提交,并释放了所有资源。 这样,即使在分布式系统中,事务的一致性和原子性也得到了保证。
6.三阶段提交啥情况下会出现事务不一致,举例生动形象的详细描述
三阶段提交(3PC)协议是分布式事务处理中用来保证事务的原子性和一致性的一个协议。它在两阶段提交(2PC)的基础上增加了一个预提交阶段,以减少阻塞并提供超时机制。尽管3PC提供了一些改进,但在某些情况下仍然可能出现事务不一致的问题。
事务不一致的情况
- *网络波动或超时导致的消息丢失
- 假设我们有三个参与者A、B和C,它们都参与了一个分布式事务。事务协调者是D。
- 在预提交阶段,协调者D向A、B、C发送预提交请求,并等待它们的确认。
- 如果A和B收到了预提交请求并确认,但C由于网络问题没有收到预提交请求,此时D可能会因为超时而决定中断事务。
- 然而,如果A和B在超时后决定提交事务(因为它们认为预提交已经成功),而C由于没有收到预提交请求而没有提交,这就导致了事务的不一致。
- *协调者故障
- 在预提交阶段,如果协调者D在发送预提交请求后但在收到所有确认之前发生故障,那么参与者A、B、C将无法确定是否应该提交事务。
- 如果D在故障前已经向A发送了预提交请求,A可能会决定提交事务,而B和C可能因为未收到预提交请求而保持等待或回滚,这同样会导致不一致。
- *参与者故障
- 如果在预提交阶段,参与者A在收到预提交请求后但在发送确认之前发生故障,协调者D可能无法确定A的状态。
- 如果D决定继续提交事务,而A实际上由于故障而无法提交,这将导致不一致。
生动形象的例子
想象一下,你和你的朋友们正在计划一个聚会。这个聚会的成功需要三个关键因素:你(参与者A)、你的朋友甲(参与者B)和你的朋友乙(参与者C)的同意和参与。
- 第一阶段(准备阶段): 你作为组织者,打电话给甲和乙,询问他们是否愿意参加聚会。这相当于协调者询问参与者是否准备好。
- 第二阶段(预提交阶段): 甲和乙都表示愿意参加,但你在联系乙的时候电话突然断线了,你不确定乙是否收到了你的信息。这相当于协调者在发送预提交请求后无法确认所有参与者的状态。
- 第三阶段(提交阶段): 由于你不确定乙的情况,你决定等待一段时间再做决定。如果甲在等待期间误以为聚会已经确定,开始准备,而乙因为没有收到最终确认而没有准备,那么聚会的计划就会出现混乱,这就像分布式事务中的不一致。 在这个例子中,如果最终你决定取消聚会,但甲已经开始了准备,这就相当于分布式事务中的不一致状态。3PC协议通过增加超时机制和预提交阶段来减少这种情况的发生,但在网络问题或系统故障的情况下,仍然可能面临事务不一致的风险。"
四、TCC(Try-Confirm-Cancel)
TCC(Try-Confirm-Cancel)通过在分布式系统中的不同节点上执行预检、确认和取消三个阶段的操作来实现事务的一致性。以下是TCC实现分布式事务一致性的具体步骤和示例
1、实现步骤
- Try阶段 - 在所有参与分布式事务的节点上执行业务逻辑的预检查,如资源检查和预留。 - 所有节点的Try操作必须成功,事务才能继续进行。
- Confirm阶段 - 如果所有Try操作成功,所有节点将执行实际的业务操作,如数据库更新,以提交事务。 - 所有节点的Confirm操作必须成功,以确保事务的一致性。
- Cancel阶段 - 如果任何一个节点的Try操作失败,所有节点将执行取消操作,回滚所有已经执行的Try阶段的操作,释放预留的资源。
2、示例:假设一个电子商务平台需要处理一个跨多个服务的订单,涉及库存服务、账户服务和订单服务。
库存服务(节点1)- Try: 检查商品库存是否足够。- Confirm: 减少库存数量。- Cancel: 如果事务失败,增加库存数量以回滚。
账户服务(节点2)- Try: 检查用户账户余额是否足够。- Confirm: 从用户账户扣除相应金额。- Cancel: 如果事务失败,将扣除的金额加回用户账户。
订单服务(节点3)- Try: 记录订单详情,但不提交数据库事务。- Confirm: 提交订单数据库事务,更新订单状态为已支付。- Cancel: 如果事务失败,删除未完成的订单记录。
3、TCC分布式事务流程
- 发起事务
- 用户发起购买请求,订单服务开始一个分布式事务。
- Try阶段
- 订单服务向库存服务发送Try请求,检查库存。
- 同时向账户服务发送Try请求,检查账户余额。
- 两个服务执行Try操作并返回结果。
- 判断Try结果
- 如果库存和账户余额都足够,订单服务记录订单详情但不提交数据库事务。
- Confirm阶段
- 订单服务向库存服务发送Confirm请求,库存服务减少库存并提交事务。
- 向账户服务发送Confirm请求,账户服务扣除金额并提交事务。
- 订单服务提交订单数据库事务,更新订单状态。
- 事务提交 - 所有服务的Confirm操作成功,分布式事务完成。
- 异常处理
- 如果任何一个Try操作失败,订单服务将向所有服务发送Cancel请求。
- 库存服务增加库存,账户服务加回金额,订单服务删除订单记录。
4、一致性保障
- TCC模式通过在所有相关服务上执行Try操作来确保事务的预检查。
- 只有所有Try操作都成功,才会进入Confirm阶段,否则进入Cancel阶段。
- 通过这种机制,TCC确保了分布式事务的一致性,即使在网络分区或服务故障的情况下也能保持数据的一致状态。
5、注意事项
- TCC要求所有操作都是幂等的,以确保在重试或回滚时不会影响系统状态。
- 在实际应用中,可能需要引入事务协调器来管理Try、Confirm和Cancel的流程。
- 需要考虑超时机制和幂等性保证,以处理网络延迟或服务故障。"
6、TCC为啥要求所有操作都是幂等的?举个形象生动的例子说明下
TCC(Try-Confirm-Cancel)要求所有操作都是幂等的,主要是因为在分布式系统中,网络通信和远程服务调用存在不确定性,如网络延迟、服务故障等,这可能导致事务的某个阶段被重复执行。
如果操作不是幂等的,那么重复执行相同的操作可能会导致数据不一致或系统状态错误。
1、幂等性的定义
幂等性意味着无论操作被执行一次还是多次,其结果都是相同的。在分布式事务中,这保证了即使在失败和重试的情况下,系统状态也不会受到影响。
2、为什么需要幂等性
- 保证数据一致性:在分布式系统中,如果一个操作因为网络问题被重复发送或执行,幂等性可以确保这些重复操作不会对数据产生副作用。
- 容错性:在发生故障时,系统可能需要重试操作。幂等性确保重试不会破坏事务的一致性。
- 简化设计:幂等性简化了分布式事务的设计,因为它减少了需要处理的异常情况。
3、生动的例子
想象一下你在餐厅点餐的场景
- Try阶段:你告诉服务员你想点的菜品(““我想点一份宫保鸡丁””)。服务员检查菜单并确认菜品可用,但还没有下单到厨房。
- Confirm阶段:如果服务员确认了你的订单,他会将你的点餐信息正式发送到厨房(““宫保鸡丁一份,下单到厨房””)。厨房开始准备你的菜品。
- Cancel阶段:如果服务员发现宫保鸡丁已经售罄,他会告诉你无法提供这道菜,并取消之前的点餐尝试。 现在,假设因为餐厅内通讯设备问题,你的点餐信息被重复发送了两次到厨房- 幂等性重要性:由于点餐操作是幂等的,即使厨房收到了两次相同的点餐信息,他们也会意识到这是同一个订单,只会为你准备一份宫保鸡丁,而不是两份。这样,即使你的点餐信息被重复发送,你也不会因此得到多份菜品,保证了你的订单数据一致性。 如果没有幂等性,可能会出现以下情况- 服务员重复告诉厨房你的点餐信息,导致厨房为你准备了多份宫保鸡丁,这不仅造成了食材的浪费,也给你造成了困扰(比如需要支付更多的费用)。 在分布式系统中,幂等性确保了即使在通信不可靠的情况下,每个节点的状态也能保持一致,避免了因重复操作导致的数据错误或资源浪费。"
五、Seata几种模式的区别
Seata 提供了几种分布式事务解决方案,如 XA模式、AT 模式、TCC 模式和 Saga 模式)有以下主要区别:
- XA 模式: XA 模式基于 XA 规范实现的分布式事务,它依赖于关系型数据库的本地事务支持 XA。 XA 模式的事务具有强一致性,满足 ACID 原则,但性能较差,因为一阶段需要锁定数据库资源,等待二阶段结束才释放。
- AT 模式: AT 模式是一种无侵入的分布式事务解决方案。用户只需要关注业务 SQL,Seata 框架会自动处理事务的二阶段提交和回滚操作。
- 在一阶段,Seata 会拦截业务 SQL,解析 SQL 语义,找到要更新的业务数据,并保存快照(before image 和 after image),然后执行 SQL 更新数据,并生成行锁,整个过程在一个数据库事务内完成,确保原子性。
- 在二阶段提交时,Seata 只需删除一阶段保存的快照数据和行锁,完成数据清理。 在二阶段回滚时,Seata 会使用 before image 来还原业务数据,并校验脏写。
- TCC 模式:Cancel)需要用户根据自己的业务场景实现 Try、Confirm 和 Cancel 三个操作。 用户在一阶段执行 Try 操作进行资源检测和预留,在二阶段提交时执行 Confirm 操作,在二阶段回滚时执行 Cancel 操作释放资源。
- Saga 模式: Saga 模式适用于长事务处理,它通过服务编排,将一个长事务拆分成多个本地事务,并由一系列的补偿操作来保证事务的最终一致性。 Saga 模式不依赖于数据库的本地事务,适用于业务流程长且需要保证事务最终一致性的业务系统。 每种模式都有其适用场景,AT 模式适用于不希望对业务进行改造的场景,几乎 0 学习成本,而 TCC 模式适用于对性能有高要求的场景,Saga 模式适用于业务流程长且需要保证事务最终一致性的系统。
版权归原作者 Dylanioucn 所有, 如有侵权,请联系我们删除。