消息队列解决分布式事务
-
本地消息表:通常处于同一张数据表,通过事务触发器就能实现,但无法解决两张表处于不同的数据库问题
begin transaction: update User set account = account - 100 where userId = 'A' insert into message(userId,amount,status) values('A',100,1) commit transaction
-
分布式事务-两阶段提交消息:
-
基本原理:通过TC事务协调器,分别去确认A,B,C事务的发生,全部成功则使用TC进行提交,否则的话则进行abort
-
缺点:涉及多次节点提交,通信时间太长;锁定的资源也变得更多。
-
-
分布式事务 - 业务方自己实现(使用消息队列来避免分布式事务),例子:B事务伴随着A事务的发生
-
基本原理:将创建事务和发布事务分成两步操作,创建事务A会在本地数据库存入消息数据库M进行记录,而并不发送消息至B;
当A事务完成时,再将M发送至B;
B中通过M_Apply表查找这一消息是否已经操作,未操作则触发操作,或者抛弃;
B事务完成后,将消息发送至A,A再在库里面删除M表;
即需要存在的表有:要执行的A表和B表,M表(存储A的事务消息发起,存在于A数据库)和M_apply(校验M消息,存在于B数据库)
//表M的存在是实现业务与消息的耦合与解耦 begin transaction update A set amout=amount - 1000 where userid=1 insert into message(userid,amount,status) values (1,1000,1) end transaction commit; //表M_apply的存在解决消息的重复提交 for each msg in queue begin transaction select count(*) as cnf from message_apply where msg_id=msg.msg_id; if cnt==0 then update B set amount = amount + 10000; where userId =1; insert into mssage_apply(msg_id) values (msg.msg_id); //如果应用则将m中数据的id插入mssage_apply end transaction1 commit
缺点:需要设计DB消息表,同时还需要一个后台任务,不断扫描本地消息。导致消息的处理和业务逻辑耦合额外增加业务方的负担。
-
-
分布式事务 - RocketMQ 事务消息
- 基本原理:
- 事务发起方首先发送 prepare 消息到 MQ。
- 在发送 prepare 消息成功后执行本地事务。
- 根据本地事务执行结果返回 commit 或者是 rollback。
- 如果消息是 rollback,MQ 将删除该 prepare 消息不进行下发,如果是 commit 消息,MQ 将会把这个消息发送给 consumer 端。
- 如果执行本地事务过程中,执行端挂掉,或者超时,MQ 将会不停的询问其同组的其他 producer 来获取状态。
- Consumer 端的消费成功机制有 MQ 保证
- 基本原理:
-
如果事务消息不能够解决以上存在的问题,比如发送端发送成功但接受端一直失败,此时状态下,应当使用人工介入进行回滚。但回滚代价巨大,应尽量避免。
-
目前较多的分布式事务解决方案:
-
结合MQ消息中间件实现可靠传输(目前电商最为流行的方式)
-
TCC补偿性事务解决方案
-
最大努力通知型方案
第一种方案:可靠消息最终一致性,需要业务系统结合MQ消息中间件实现,在实现过程中需要保证消息的成功发送及成功消费。即需要通过业务系统控制MQ的消息状态
第二种方案:TCC补偿性,分为三个阶段TRYING-CONFIRMING-CANCELING。每个阶段做不同的处理. TRYING阶段主要是对业务系统进行检测及资源预留
CONFIRMING阶段是做业务提交,通过TRYING阶段执行成功后,再执行该阶段。默认如果TRYING阶段执行成功,CONFIRMING就一定能成功。
CANCELING阶段是回对业务做回滚,在TRYING阶段中,如果存在分支事务TRYING失败,则需要调用CANCELING将已预留的资源进行释放。
第三种方案:最大努力通知xing型,这种方案主要用在与第三方系统通讯时,比如:调用微信或支付宝支付后的支付结果通知。这种方案也是结合MQ进行实现,例如:通过MQ发送http请求,设置最大通知次数。达到通知次数后即不再通知。
-
-
主流的开源MQ(ActiveMQ、RabbitMQ、Kafka、redis)RocketMQ(alibaba)