分布式事务
关键词
-
强一致性,最终一致性
-
两阶段提交协议(Two-phase Commit,2PC)
- TC(transaction client)发起全局事务时,向所有的子事务发送prepare消息,子事务执行但是不commit,成功返回yes,失败NO
- TC收到所有子事务的YES,再发送commit消息,让所有子事务commit
每一步都是先写log,再执行; 这是最重要的,帮助全局事务/子事务的rollback
单点故障:TC节点不可用(事务manager节点),则全局不可用;
事务响应时间长;
-
全局事务,子事务
-
CAP,一致,可用,分区容错
-
BASE,Basically Available(基本可用),Soft state(软状态),Eventually consistent(最终一致性)
-
3PC三阶段提交;增加cancommit问询阶段,和超时机制
- 先问询,canCommit
- preCommit(不提交),并undo-log
- doCommit 或者 超时Commit
同样每一步都是先log,再执行www.jianshu.com/p/d77a6bb0b…
- TCC(补偿事务)
- try,业务检测和
预留或者锁定资源 - comfirm,业务执行
- cancel,业务回滚
业务复杂度增加。
解决方案
try-catch重试
业务对数据的一致性要求不高,最终一致性就满足需求
- 重试成功,ok
- 重试失败,记录 + 异步处理(MQ\JOB)
// a.servie
try{
b.service()
}catch(e){
mq/job/log/retry =>
}
MQ编程模式
使用消息队列解决分布式事务(最简单的解决方案).用例:实现A->B转账(A,B是不同微服务)
--1. A账户扣钱,并生成扣减记录(单机事务)
update `tab_a` set a = a - 100;
insert `tab_a_record`(txid_a,100,`try`);
--2.发送消息,mq(事务消息rocketmq)
--3. B账户加钱(单机事务,`txid_a`实现幂等)
update `tab_b` set b = b + 100;
insert `tab_b_record`(txid,100,txid_a,`confirm`);
--4.统一订单状态(最终一致性)
update `tab_a_record` set (txid_a,'finished' || 'rollback');
--5.增加补偿|监控
- 分布式事务解决的关键:事务原子性,幂等;
- 用例只涉及了2个微服务,比较简单
- 通过
记录表实现基本的事务日志,实现最终一致 txid_a在各个微事务之间透传。
rocketmq事务消息
- 基于2pc(2 phase commit)实现
- 原子性的实现:本地事务的执行和消息的发送
????? transactionCheckMax=15事务消息的默认检查次数=15,transactionTimeout事务消息检查时间间隔, 默认超过次数则丢弃消息,并log,通过AbstractTransactionCheckListener可以自定义实现- 代码只在producer端有事务要求
事务执行的过程
- 发送 prepare 消息到消息中间件。
- 发送成功后,执行本地事务。
- 如果事务执行成功,则 commit,消息中间件将消息下发至消费端。如果事务执行失败,则回滚,消息中间件将这条 prepare 消息删除。
- 消费端接收到消息进行消费,如果消费失败,则不断重试。
总结: 维护producer发送消息+本地事务原子性,consumer重试消费消息
TCC 事务
- 先是服务调用链路依次执行
Try逻辑。 - 如果都正常的话,TCC 分布式事务框架推进执行
Confirm逻辑,完成整个事务。 - 如果某个服务的 Try 逻辑有问题,TCC 分布式事务框架感知到之后就会推进执行各个服务的
Cancel逻辑,撤销之前执行的各种操作。
seata 事务
SEATA通过设计全局事务统一协调各个单一事务。
架构设计
- TC:管理全局事务和微事务的status,生成
XID - TM: 定义并管理全局事务
- RM:管理微事务,注册微事务的状态到TC
支持的事务模式
- AT
- TCC
AMA
-
业务幂等性如何实现?
-
a->b->c,d;如果某个服务宕机(c),如何保证重启事务继续执行.
-
分布式事务的场景
- 分布式服务 + 单一数据库
- 分布式服务 + 分布式数据库
-
MYSQL的单机事务通过日志(commit-log)实现2PC