事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。事务的四个特征(ACID)
传统的单体应用借助关系数据库的ACID特性能很好的保证数据一致性,但是随着系统的发展,由单体应用向分布式、微服务的演变,传统的事务无法协调多个服务、多个数据库之间的事务,是之达到统一提交或统一回滚状态。
分布式事务是指事务的参与者、资源服务器以及事务管理器分别位于不同的分布式系统的不同结点之上,一次分布式事务涉及到多个系统、数据库之间的协调,保证分散在各结点的事务操作要么全部成功,要么全部失败。
CAP理论对分布式系统特性做了高度抽象,形成了三个指标:
在分布式系统中,只要有网络交互就一定存在延迟和数据丢失,这是我们必须接受的,同时还要保证系统不能挂掉。节点间的分区故障是必然存在的,也就是说分区容错性(P)是必须要保证的。现在就只剩下一致性(C)和可用性(A)之间选择一个了。
当选择了一致性(C),系统能保证读取到最新的数据,但节点之间数据同步需要时间,就会导致在读取数据时发生阻塞而超时,为了不破坏一致性,可能会因为无法响应最新数据而返回错误信息。
当选择了可用性(A),系统将始终响应客户端的查询,返回特点的信息,但如果发生网络分区、或者数据延迟,返回的数据并不一定是全局最新数据。
由于CAP理论我们只能在C、A、P中选择其中两个。BASE理论是对CAP中一致性和可用性权衡的结果。它的核心思想是,如果非必须,不推荐使用强一致性,鼓励优先考虑可用性,根据业务的场景来实现弹性的基本可用,以及实现数据最终一致性。
BA:Basic Available基本可用,当系统出现不可预知的故障,允许损失部分可用性,但不是完全不可用。
S:Soft State柔性状态,数据存在一个中间状态,并该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
E:Eventual Consisstency最终一致性,经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
事务的发起者提出一个request(比如用户下单购买某个商品),要求其依赖的服务(事务的执行者)本地执行业务逻辑。执行成功本地事务不提交但要告诉发起者本地已经执行成功;执行失败执行者告诉发起者本地作业执行失败
事务发起者根据事务发起阶段收集到的信息决定提交/回滚
如果全部执行者都反馈成功那么发起者通知所有执行者提交事务
如果存在执行者反馈失败,则发起者通知所有参与者取消事务
事务协调者向参与者发送事务执行请求,询问是否可以完成指令,参与者只需要回答是不是即可,不需要做正真的事务操作,这个阶段会有超时终止机制。
事务协调者会根据参与者的反馈结果决定是否继续执行,如果在询问阶段所有参与者都返回可以执行操作,则事务协调者会向所有参与者发送PreCommit请求,参与者收到请求后会写redo和undo日志,执行事务操作但是不提交事务,然后返回ACK响应等待事务协调者的下一步通知。如果询问阶段任意参与者返回不能执行操作的结果,那么事务协调者会向所有参与者发送事务中断请求。
这个阶段也会存在两种结果,根据上一步骤的执行结果来决定DoCommit的执行方式。如果每个参与者在PreCommit阶段都返回成功,那么事务协调者会向所有的参与者发起事务提交指令。反之,如果参与者中的任一个参与者返回失败,那么事务协调者就会发起中转指令来回滚事务。
TCC是将事务分为Try(尝试)、Confirm(确认)和Cancel(取消)三个阶段。每个阶段有开发者自己控制,避免了长事务的问题。
TCC事务模式需要考虑下面几个问题:
空回滚:当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。
悬挂:对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。
幂等:Confim、Cancel阶段不论网络原因或者业务异常,都会根据日志记录重复执行操作,所以需要保持接口的幂等性,也就是最多执行一次
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
Seata由三种角色组成:
维护全局和分支事务的状态,驱动全局事务提交或回滚。
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有****主流的数据库****都对 *XA 规范* 提供了支持。
1 TM负责定义全局事务的边界,向TC申请,开启一个全局事务;
2 全局事务创建成功后,生成全局唯一的XID;
3 XID会在微服务请求链路上下文中传播;
4 RM向TC注册分支事务,并归属到XID对应的全局事务进行调度;
5 TM向TC发起相应XID的全局事务提交或回滚决议;
6 TC完成对XID管理的全部分支事务提交或回滚的调度;
我们看下下面的demo,有order服务负责创建订单,并且通过Fegin调用库存服务扣库存,如果库存不足则回滚。
seata-order
@GlobalTransactional(rollbackFor = {Exception.class})
@Transactional
public String createOrder(String userId, String itemId, Integer num) {
Order order = new Order();
order.setUserId(userId);
order.setItemId(itemId);
order.setNum(num);
orderRepository.save(order);
// 扣除库存
inventoryFeign.adjust(itemId, num);
return "创建成功";
}
seata-inventory
public void adjust(String itemId, Integer num) {
Inventory inventory = inventoryDao.findByItemId(itemId);
if(inventory.getInventory() < num){
throw new RuntimeException("库存不足");
}
inventory.setInventory(inventory.getInventory() - num);
inventoryDao.save(inventory);
}
库存表中有商品编号为10000的商品,库存为10。以debug的方式启动服务,在扣库存的地方打上端点,你会发现在扣完库存,库存的数量并没有减少,订单也没用创建,并且行记录也被锁住,直到事务提交,才能看到订单创建和库存扣减,如果扣除不足,订单也会回滚。
前面我们提到过,AX是基于数据库的事务特征,属于两阶段提交,所以在整体事务提交前,各服务数据不可见。
XA模式的优点:
事务的强一致性,满足ACID原则。
常用数据库都支持,实现简单,并且没有代码侵入
XA模式的缺点:
AT模式是基于两阶段协议的演变:
修改为AT模式只需要修改配置:
seata.dataSourceProxyMode=AT
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) E
AT模式需要新增一张undo_log表,用来记录执行SQL的回滚SQL,比如业务执行了INSERT操作,就会记录一下对应的DELETE语句。所以AT模式下是不依赖与数据库的事务。
AT模式的优点:
AT模式的缺点:
TCC 模式,不依赖于底层数据资源的事务支持:
AT模式的缺点:
TCC 模式,不依赖于底层数据资源的事务支持: