分布式事务
分布式的事务是指事务的参与者、支持事务的服务器、资源服务器及事务管理器分别位于不同的分布式系统的不同节点之上;
-
问题
支付宝账户表:A (id, user_id, amount)
余额宝账户表:B (id, user_id, amount)
用户的 user_id = 1,从支付宝转帐1万快到余额宝分为两个步骤:
支付宝表扣除1万:
UPDATE A SET amount = amount - 10000 WHERE user_id = 1;
余额宝表增加1万:
UPDATE B SET amount = amount + 10000 WHERE user_id = 1;
如何保证数据一致性呢?
-
一致性
方案:
1. 两阶段提交
XA 是一个两阶段提交协议,该协议分为以下两个阶段:
第一阶段:事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.
第二阶段:事务协调器要求每个数据库提交数据。
2. 异步确保
基本思路就是:
消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。
消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。
生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。
3. MQ事务消息
2PC Message Queue
利用支持事务的消息队列,比如RocketMQ
三阶段
a. Prepared 消息,会拿到消息的地址
b. 执行本地事务
c. 通过第一阶段拿到的消息地址去访问消息,并修改状态;
RMQ会要求生产者实现check接口,并会告知RMQ自己本地事务是否执行成功,RMQ会定时轮训所有pre状态的消息,并调用对应的check接口,以决定此消息是否可以提交
当支付宝账户扣除1万后,我们只要生成一个凭证(消息)即可,这个凭证(消息)上写着“让余额宝账户增加 1万”,只要这个凭证(消息)能可靠保存,我们最终是可以拿着这个凭证(消息)让余额宝账户增加1万的,即我们能依靠这个凭证(消息)完成最终一致性。
4. 补偿交易(TCC)
针对每次操作,都注册一个与其对应的补偿操作;一般两个操作都在同一个事务。
三个阶段:
a. Try 阶段主要对业务系统做检测及资源预留;
b. Confirm 阶段对业务系统做确认提交,try阶段执行成功并开始执行confirm阶段时;
c. Cancel 阶段主要对业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放;
需要注意:
空回滚、防悬挂
5. 消息重试
针对消息一次性传递失败的场景;
6. Sagas事务模型
又叫长时间允许的事务 long-running-transaction
描述的是另外一种在没有两阶段提交的情况下解决分布式系统中复杂的业务事务问题;
一种基于sagas机制的工作流事务模型
该模型其核心思想就是拆分分布式系统中的长事务为多个短事务,或者叫多个本地事务,然后由 Sagas 工作流引擎负责协调,如果整个流程正常结束,那么就算是业务成功完成,如果在这过程中实现失败,那么Sagas工作流引擎就会以相反的顺序调用补偿操作,重新进行业务回滚。
Sagas 事务模型最重要的一个部件就是工作流或者你也可以叫流程管理器(Process Manager),工作流引擎和Process Manager虽然不是同一个东西,但是在这里,他们的职责是相同的。
7. Seata 2PC
Seata 实现 2PC 与传统 2PC 的差别
• 架构层次方面:传统 2PC 方案的 RM 实际上是在数据库层,RM 本质上就是数据库自身,通过 XA 协议实现,而 Seata 的 RM 是以 jar 包的形式作为中间件层部署在应用程序这一侧的。
两阶段提交方面:传统 2PC无论第二阶段的决议是 commit 还是 rollback ,事务性资源的锁都要保持到 Phase2 完成才释放。而 Seata 的做法是在 Phase1 就将本地事务提交,这样就可以省去 Phase2 持锁的时间,整体提高效率