12 分布式事务

时间:2025-01-20 09:38:58

分布式事务产生的原因

我们拿mysql数据库来说,当数据库为单体数据库的时候,我们打开事务,执行sql为预执行阶段,最后commit时通过日志控制最终全部提交后存储到磁盘中,如果commit失败,可以通过日志控制回滚回来,但是当我们的数据库实例为多个的时候,不同的数据源,我们的日志已经无法控制落入磁盘的数据正常的回滚。当我们分库的时候旧无法通过传统的方式控制事务的正常运转,这个时候就产生了分布式事务的问题。

分布式事务出现的场景

电商平台下单的场景:当我们的库存信息和生成的订单信息通过垂直拆分不在一个库的时候涉及到分布式事务的问题
电商平台的支付业务:买家和卖家的数据库不在同一个库里面,当买家支付金额扣减和卖家收款金额的增加涉及到分布式事务问题

分布式事务一致性的强弱

分布式事务在不同的业务场景有不同的处理方案。一般分为强一致性、弱一致性,最终一致性的场景。

强一致性要求当一个库的数据更新后,任何时刻任何节点的数据库实例在读取操作能够获取最新的值。这种一致性保证了数据的准确性和可靠性,但是是以消耗系统的延迟和性能作为代价。强一致性需要写入操作等待所有节点确认才能返回成功。

弱一致性允许部分数据库实例更新数据后,其他节点的数据在读取操作不一定是最新的值。这种一致性在牺牲数据的不确定性和不一致性,保证了系统整体的性能和可用性

最终一致性通过一些补偿和重试机制,或者是人工干预的情况下保证数据最终一致,在一段时间内可能会有读取不同节点数据库数据不一致的情况出现

常见的分布式事务解决方案

1. 两阶段提交 2PC
两阶段提交属于强一致性事务。计算机相关的技术是逻辑的体现,我们不要硬搬技术实现,需要看实际的业务场景选择对应的技术

我们假设一个场景: 我们组织一次旅游,每个人都选择了不同的旅游地点,因此而争论不休。这个时候我们需要一个调停者的出现,调停者根据大家的诉求选择了几个备选的旅游地点,如果大家都统一则下发通知到某一个地点去旅游,如果有一方不同意则取消并重新选择。这就是我们两阶段提交的逻辑雏形

2PC分为准备阶段和提交阶段
准备阶段:
事务的管理者协调事务的参与者,询问是否能够执行分布式事务的操作,各个事务的参与者在收到管理者消息时,锁定相关的资源,并应答管理着已经准备好了可以提交。
提交阶段:
事务的管理者在收到所有参与者都可以提交的消息时,下达提交事务的指令,事务的参与者提交事务。如果有参与者返回不能提交事务则下达回滚的指令。

两阶段提交的优点:强一致性,适用于对数据的一致性要求非常高的场景
缺点:性能差,存在单点问题,可能导致资源锁定的问题。

2. 三阶段提交 3PC
前面讲到的两阶段提交的缺点时讲到存在单点问题(管理着服务挂掉,或者是网络问题导致下发的提交或回滚的指令未执行),如果出现单点问题,那么分布式事务的参与者锁定的资源长时间得不到释放。
所以我们在两阶段提交加入了预提交和超时提交的阶段

三阶段提交:
cancommit阶段:事务管理者询问各节点是否可以执行事务,参与者检查自己的环境并告知管理着是否能够参与事务,但并不锁定资源
precommit阶段:如果参与者应答可以参与事务,管理着则下达预提交指令,这个时候分布式事务的各个参与者将执行事务操作把undo和redo日志写入日志中,但并不提交事务,而是告知管理着都准备好了的消息。
docommit阶段:所有参与者提交准备好了的消息后,管理者发送提交事务消息。正式提交事务。如果因为网络问题或其他原因参与者未收到消息在等待一段时间后提交事务

三阶段提交在两阶段提交基础上增加了cancomit阶段,减少部分参与者不能参与事务的资源锁定问题。
三阶段提交通过分散协调者的角色和责任,减少单点故障的问题,适用于网络环境比较差的场景。但是增加了各个节点的逻辑复杂性。

3. 本地消息表
在同一个事务中把业务数据写入本地消息的存储,然后通过消息或者通过定时任务确保消息的可靠传递,以达到最终一致的效果。‘
本地消息表实现起来简单,稳定。但引入了额外的存储开销。

4. 最大努力通过
最大努力通知适用弱一致性的业务场景。例如:电商平台下单后,邮件通知用户下单成功。
在用户下单成功后,发送消息并记录发送状态。
如果发送失败,定时重新尝试。
业务方接收到消息时,需要做幂等操作。

该方案实现简单,适用于对一致性要求不高的场景。无法保证强一致性,存在消息延迟的情况

5. rocketmq的消息事务
rocketmq通过prepare消息和回查机制实现分布式事务
大体流程如下:
系统业务方发送半事务消息:系统业务方在执行本地事务之前,先向消息队列发送一条半事务消息(表示本地事务未完成)
broker存储半事务消息:broker收到半事务消息以后,将其存储,并向业务方返回结果
系统业务方执行本地事务:业务方执行本地事务
业务方提交事务状态:业务方在执行完本地事务以后会把事务状态(提交或回滚)的确认消息给broker
broker会查本地事务:如果broker在设定的时间内(延时消息)未收到事务确认状态,会回查业务方的事务状态
业务方处理回查请求:回应broker的回查请求
broker处理回查结果:broker根据业务方的事务状态决定是提交或回滚事务消息,并将处理后的消息更新到实际的消息队列中,供消费者消费消息

rocketmq通过2PC+事后的补偿机制确保消息的一致性,避免因网络故障造成事务的不一致性。

我们看看2PC、3PC、和rocketmq的事务消息都是在解决异常情况出现的逻辑不严密的问题,也是生活中也会遇到的场景。形成一个严谨的逻辑体系,不管是开发还是架构设计最后都是逻辑,也就是我开篇提到的一个系统的逻辑体系的建立。

rocketmq的事务消息性能好,易于扩展,但是需要依赖mq服务,增加了业务的复杂性。

6. TCC(try-confirm-cancel)
TCC通过显示编程的方式,将分布式事务拆解为三个步骤
try:资源预留 confirm:正式提交 cancel:回滚操作

具体的实现:
利用阿里的seata TCC模块实现,开发时需要编写try、confirm、cancel方法。

优点:灵活性高,适用于高性能的场景 缺点:开发成本高,需要自己处理幂等和一致性

7. 状态机
使用状态机管理事务的生命周期。包括事务的初始、处理中、完成、失败等
状态变更通过事件触发。结合redis、数据库、zookeeper实现分布式的状态管理

实现步骤:
首先定义状态机模型
再实现事件触发的逻辑
监控状态的流转

优点:扩展性强,适合复杂业务
缺点:实现复杂、调试和测试复杂