**现有的分布式事务存在两大理论依据:**
- **CAP定律**
这个定理的内容是指的是在一个分布式系统中、Consistency(强一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
1. **一致性(C-Consistency)**:在分布式系统中所有的数据备份,在同一时刻是否有同样的值(强一致性).
2. **可用性(A-Availability)**:即服务一直可用,而且是正常响应时间。好的可用性主要是指系统能够很好的为用户服务,不出现用户操作失败或者访问超时等用户体验不好的情况。
3. **分区容错性(P-Partition tolerance)**:以实际效果而言,分区相当于对通信时限的要求.系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A做出抉择(分区状态可理解为部分机器不连通,如机器挂了或单机房故障),而且在分布式系统中,P是必选项,而不是可选项.
- **BASE理论**
BASE是Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。
1. **基本可用**:是指分布式系统在出现不可预知故障的时候,允许损失部分可用性----注意,这绝不等价于系统不可用。比如:
1. 响应时间上的损失。正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒
2. 系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面
2. **软状态**:指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时
3. **最终一致性**:最终一致性强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
## 当下流行的分布式事务解决方案
### TX-LCN
TX-LCN定位于一款事务协调性框架,框架其本身并不操作事务,而是基于对事务的协调从而达到事务一致性的效果;LCN并不产生事务,LCN只是事务的搬运工.
TX-LCN包含两个模块:Tx-Client(TC) Tx-Manager(TM).TC即作为微服务下的依赖,TM则是独立的服务.
原理图:
![img](https://www.txlcn.org/img/docs/yuanli.png)
核心步骤:
- 创建事务组
是指在事务发起方在调用业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupID的过程.
- 加入事务组
添加事务组是指参与方在执行完业务方法之后,将该模块的事务信息通知给TxManager的操作.
- 通知事务组
是指在发起方执行完业务方法之后,将发起方执行结果的状态通知给TxManager,TxManager根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方.
模式:
1. LCN(lock-confirm-notify)
- 原理
- LCN事务控制原理是由事务模块TxClient下的代理连接池与TxManager的协调配合完成的.TxClient的代理连接池实现了javax.sql.DataSource接口,并重写了close方法,事务模块在提交关闭以后,TxClinet连接池将执行"假关闭"操作,等待TxManager协调完成事务以后在真正关闭连接.
- 优缺点
1. 性能优秀,可靠性强.
2. 兼容性强,支持所有的关系型数据库事务,支持多数据源.
3. 业务入侵性低,使用时只需添加相应注解即可.
4. 需额外部署Tx-Manager节点,增加相应的运维成本.
5. 由于需要lock资源这种处理方式,在更新某几个热点商品时LCN的性能衰减量大于TCC模型.
6. 服务超时时,会造成其他服务的资源被锁住.如支付服务超时,相关商品的库存会一直无法操作.
2. TCC(try-confirm-cancel)
- 原理
- TCC事务机制相对于传统事务机制(XA-2PC),他不依赖于资源管理器(RM)对XA的支持,而是通过对业务逻辑的调度来实现的分布式事务.主要分为三个步骤:Try-尝试执行业务,Confirm-确认执行业务,Cancel-取消执行业务.
- 优缺点
1. 该模式代码侵入性极高,需要每个业务写三个步骤的操作.
2. 数据的一致性几乎完全由开发者控制,对业务开发要求难度高.
3. 该模式对本地有无事务都可以支持.
3. TXC(taobao-transaction-constructor)
- 原理
- 命名来源于淘宝中间件团队,实现原理是在执行SQL之前,先查询SQL影响的数据,然后保存执行的SQL快照信息和创建锁.当需要回滚时就采用这些
- 优缺点
1. 代码侵入性低.
2. 该模型仅限于对支持SQL的模块支持.
3. 该模式由于在每次执行SQL之前都要查询影响的数据,因此消耗的资源与时间要比LCN模式要多.
4. 该模式不会占用数据库的连接资源.
[参考]: http://springcloud.cn/view/374#Title-H4-108
### Seata(GTS开源版本)
2014 年,阿里中间件团队发布 **TXC(Taobao Transaction Constructor)**,为集团内应用提供分布式事务服务.
2016 年,TXC 经过产品化改造,以 **GTS(Global Transaction Service)** 的身份登陆阿里云,成为当时业界唯一一款云上分布式事务产品,在阿云里的公有云、专有云解决方案中,开始服务于众多外部客户.
2019 年起,基于 TXC 和 GTS 的技术积累,阿里中间件团队发起了开源项目 **Fescar(Fast & EaSy Commit And Rollback, FESCAR)**,后更名为Seata。
先熟悉一下相关概念:
- XID:全局事务的唯一标识,由 ip:port:sequence 组成;
- Transaction Coordinator (TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
- Transaction Manager (TM ):控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
- Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚;
原理图
![Architecture](https://camo.githubusercontent.com/6a31d57b4496e83af1a0bd76ede83657de2d9d34/68747470733a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343432303736372d343934613534643430326232643335342e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430)
过程解析:
1. TM先向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID.
2. XID在服务调用的链路上下文中传播.
3. RM向TC注册分支事务,将其纳入XID对应的全局事务的管辖.
4. TM向TC发起针对XID的全局提交或回滚决议.
5. TC调度XID下管辖的全部分支事务完成提交或回滚请求.
优缺点(包含GTS优缺点):
1. 高性能、高可靠、接入简单,可做到业务代码代码零入侵
2. 支持多种数据源
3. 兼容多种微服务框架
4. GTS无法在本地测试,只能在阿里云内网使用,或者购买专有云
5. 当前Seata未达到生产可用版本
```java
/**
Seata 在 AT 模式下稍微有点代码量的地方就是对数据源的代理指定
使用 DataSourceProxy 的目的是为了引入 ConnectionProxy ,Seata 无侵入的一方面就体现在 ConnectionProxy 的实现上,
即分支事务加入全局事务的切入点是在本地事务的 commit 阶段,这样设计可以保证业务数据与 undo_log 是在一个本地事务中.
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
@Primary
@Bean("dataSource")
public DataSourceProxy dataSource(DruidDataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
```
#### 用例:
用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:
- 存储服务:扣除给定商品的存储数量。
- 订单服务:根据购买请求创建订单。
- 帐户服务:借记用户帐户的余额。
![建ç](https://camo.githubusercontent.com/15ddc803a93baf964ee481c7864d444e727e4bcc/68747470733a2f2f63646e2e6e6c61726b2e636f6d2f6c61726b2f302f323031382f706e672f31383836322f313533363036303238313731392d30376633333336312d363031342d343130652d613562372d6334613835383537633930362e706e67)
### StorageService
```java
public interface StorageService {
/**
* deduct storage count
*/
void deduct(String commodityCode, int count);
}
```
### OrderService
```java
public interface OrderService {
/**
* create order
*/
Order create(String userId, String commodityCode, int orderCount);
}
```
### AccountService
```java
public interface AccountService {
/**
* debit balance of user's account
*/
void debit(String userId, int money);
}
```
### business logic
```java
public class BusinessServiceImpl implements BusinessService {
private StorageService storageService;
private OrderService orderService;
/**
* purchase
*/
public void purchase(String userId, String commodityCode, int orderCount) {
storageService.deduct(commodityCode, orderCount);
orderService.create(userId, commodityCode, orderCount);
}
}
public class OrderServiceImpl implements OrderService {
private OrderDAO orderDAO;
private AccountService accountService;
public Order create(String userId, String commodityCode, int orderCount) {
int orderMoney = calculate(commodityCode, orderCount);
accountService.debit(userId, orderMoney);
Order order = new Order();
order.userId = userId;
order.commodityCode = commodityCode;
order.count = orderCount;
order.money = orderMoney;
// INSERT INTO orders ...
return orderDAO.insert(order);
}
```
Seata处理过程:
![undefined](https://camo.githubusercontent.com/fbbbc543e4a52b65ec8c7577a9cc128a81f61387/68747470733a2f2f63646e2e6e6c61726b2e636f6d2f6c61726b2f302f323031382f706e672f31383836322f313534353230393135353538392d31316562653032642d373265662d343761342d393266352d3336626535346665396231372e706e67)
在使用Seata的前提下,实现分布式事物只需将 @GlobalTransactional 加在业务方法上即可:
```java
@GlobalTransactional
public void purchase(String userId, String commodityCode, int orderCount) {
......
}
```