分布式事务

面试题:在一台机器中,db本地事务如何保证?

锁、redo、undo

TM - 事务协调者

定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM - 资源管理器

管理分支事务处理的资源,与TM交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

二阶段提交

为什么引入二阶段提交?

因为每个服务无法感知其他服务的事务状况,需要协调者。

transaction

1
2
3
4
5
6
7
8
9
10
11
1 阶段
阶段一:提交事务请求
1.1事务询问
协调者节点向所有参与者节点询问是否可以执行提交操作(vote),并开始等待各参与者节点的响应。

1.2执行事务
参与者节点执行询问发起的所有事务操作,并将Undo信息和Redo信息写入日志。(注意:若成功这里其实每个参与者已经执行了事务操作)

1.3各参与者向协调者反馈事务询问的响应
各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个”同意”消息;
如果参与者节点的事务操作实际执行失败,则它返回一个”中止”消息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2 阶段
阶段二:执行事务提交
当协调者节点从所有参与者节点获得的相应消息都为”同意”时:
2.1发送提交请求
协调者节点向所有参与者节点发出”正式提交(commit)”的请求。

2.2事务提交
参与者节点正式完成操作,并释放在整个事务期间内占用的资源。

2.3反馈事务提交结果
参与者节点向协调者节点发送”完成”消息。

2.4完成事务
协调者节点受到所有参与者节点反馈的”完成”消息后,完成事务。
如果任一参与者节点在第一阶段返回的响应消息为”中止”,或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:

2.5中断事务:
发起回滚请求
协调者节点向所有参与者节点发出”回滚操作(rollback)”的请求。

2.6事务回滚
参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。

2.7反馈事务回滚结果
参与者节点向协调者节点发送”回滚完成”消息。

2.8中断事务
协调者节点受到所有参与者节点反馈的”回滚完成”消息后,取消事务。
不管最后结果如何,第二阶段都会结束当前事务。
1
2
3
4
二阶段提交的问题:
1.TM事务管理器 单点故障
2.阻塞资源
3.两个服务数据不一致

二阶段提交的过程会锁定资源不释放。

二阶段提交是为了降低服务1提交后,服务2挂了的概率。

三阶段提交

三阶段提交是基于二阶段的基础上,在最前面增加了一个询问阶段。

transaction

三阶段提交不能解决二阶段提交的所有问题,只是降低灾难发生的概率。

1
2
3
1.can commit : 执行SQL不锁定资源
2.pre commit : 执行SQL锁定资源
3.do commit : 提交SQL
1
2
3
4
5
6
7
8
三阶段提交和二阶段提交的区别:
1.降低了锁定资源的概率和时长
2.引入超时机制,同时在协调者(超时-中断事务)和参与者(超时,在pre中断,在do提交)中都引入了超时机制。
3.在第一阶段和第二阶段之前插入一个准备阶段,保证了再最后提交阶段之前各参与节点的状态是一致的。

TM事务协调者和RM资源拥有者的超时处理机制
TM:未收到反馈,给RM发中断事务的命令
RM:在三阶段,没有收到TM的命令,默认提交
1
2
3
4
5
6
7
8
9
10
11
12
Base理论介绍
BASE是Basically Availbale(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。
BASE理论是对CAP中AP的一个扩展,通过牺牲强一致性来获得可用性,当出现故障允许部分不可用但要保证核心功能可用,允许数据在一段时间内是不一致的,但最终达到一致状态。满足BASE理论的事务,我们称之为“柔性事务”。

基本可用:
分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。如电商网址交易付款出现问题来,商品依然可以正常浏览。

软状态:
由于不要求强一致性,所以BASE允许系统中存在中间状态(也叫软状态),这个状态不影响系统可用性,如订单中的“支付中”、“数据同步中”等状态,待数据最终一致后状态改为“成功”状态。

最终一致性:
最终一致是指的经过一段时间后,所有节点数据都将会达到一致。如订单的“支付中”状态,最终会变为“支付成功”或者“支付失败”,使订单状态与实际交易结果达成一致,但需要一定时间的延迟、等待。

消息队列

transaction

LCN

LCN:Lock(锁定事务单元),Confirm(确认事务),Notify(通知事务)

官方文档 TX-LCN由两大模块组成, TxClient(TC)、TxManager(TM),TxClient作为模块的依赖框架,提供TX-LCN的标准支持,TxManager作为分布式事务的控制放。事务发起方或者参与反都由TxClient端来控制。

事务控制原理

transaction

1
2
3
4
5
6
7
8
创建事务组
是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。

加入事务组
添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给TxManager的操作。

通知事务组
是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager,TxManager将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。

协调机制本质

transaction

代码应用

TM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- 创建tx-manager库
CREATE DATABASE IF NOT EXISTS `tx-manager` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
USE `tx-manager`;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_tx_exception
-- ----------------------------
DROP TABLE IF EXISTS `t_tx_exception`;
CREATE TABLE `t_tx_exception` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`mod_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`transaction_state` tinyint(4) NULL DEFAULT NULL,
`registrar` tinyint(4) NULL DEFAULT NULL,
`ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 待处理 1已处理',
`remark` varchar(10240) NULL DEFAULT NULL COMMENT '备注',
`create_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 967 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- tm  manager -->
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tm</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>

<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-txmsg-netty</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#引入配置redis和DB...这里省略

# TM事务管理器的服务端WEB访问端口。提供一个可视化的界面。端口自定义。
server.port=7970

# TM事务管理器,提供的WEB管理平台的登录密码。无用户名。 默认是codingapi
tx-lcn.manager.admin-key=admin
# 日志。如果需要TM记录日志。则开启,赋值为true,并提供后续的配置。
tx-lcn.logger.enabled=true

# 为日志功能,提供数据库连接。和之前配置的分布式事务管理依赖使用的数据源不同。
tx-lcn.logger.driver-class-name=com.mysql.cj.jdbc.Driver
tx-lcn.logger.jdbc-url=jdbc:mysql://localhost:3306/tx-manager?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
tx-lcn.logger.username=root
tx-lcn.logger.password=root
1
2
3
4
5
6
7
@SpringBootApplication
@EnableTransactionManagerServer
public class LcnTmApplication {
public static void main(String[] args) {
SpringApplication.run(LcnTmApplication.class, args);
}
}

访问http://localhost:7970登录即可

transaction

transaction

TC

1
2
3
4
5
6
7
8
9
10
11
12
<!-- rm -->
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>

<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-txmsg-netty</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
1
2
3
4
5
6
#引入配置db...这里省略
# tm配置
tx-lcn:
client:
#TM Web界面提供的ip和端口
manager-address: 127.0.0.1:8070
1
2
3
4
5
6
7
8
@SpringBootApplication
//RM的注解
@EnableDistributedTransaction
public class LcnPayApplication {
public static void main(String[] args) {
SpringApplication.run(LcnPayApplication.class, args);
}
}
1
2
3
4
5
6
7
8
9
10
11
@PostMapping("/add-pay")
@Transactional(rollbackFor = Exception.class)
//LCN注解
@LcnTransaction
public String addPay(@RequestBody TblPay bean){
//调用其他服务产生其他事务...其他服务也需要增加相关配置和@EnableDistributedTransaction、@LcnTransaction

//自身服务的事务
tblPayDao.insert(bean);
return "新增支付成功";
}

集群

TM起多个,TC配置多个地址即可.

1
2
3
tx-lcn:
client:
manager-address: 127.0.0.1:8070,127.0.0.1:8071

TCC

TCC:Try、Confirm、Cancel

transaction

1
2
3
TCC主要是通过逆向SQL来达到回滚的作用,需要编写逆向SQL,会增加业务复杂度。
所以用自带事务的中间件,比如mysql,不用TCC,用LCN。
但是如Mongo、Redis这种没有事务的数据库,可以使用TCC。

在微服务互相调度的过程中,TCC和LCN是可以混合使用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@RestController
public class OrderTccController {
@Autowired
private TblOrderDao tblOrderDao;

@Autowired
private RestTemplate restTemplate;

private static Map<String,Integer> maps = new HashMap<>();

@PostMapping("/add-order-tcc")
@Transactional(rollbackFor = Exception.class)
@TccTransaction
public String add(@RequestBody TblOrder bean){

JSONObject date = new JSONObject();
date.put("payName",bean.getOrderName()+"pay");

//调用另外服务的TCC
restTemplate.postForEntity("http://lcn-pay/add-pay-tcc",date,String.class);

tblOrderDao.insert(bean);
Integer id = bean.getId();
maps.put("a",id);

// int i = 1/0;
return "新增订单成功";
}

//不需要注解 方法名:comfirmMethod
public String confirmAdd(TblOrder bean){

System.out.println("order confirm ");
return "新增订单成功";
}

//不需要注解 方法名:cancelMethod
public String cancelAdd(TblOrder bean){
Integer a = maps.get("a");
System.out.println("a:"+a);
tblOrderDao.deleteByPrimaryKey(a);
System.out.println("order cancel ");
return "新增订单成功";
}
}

Seata

跳转至Seata文章

可靠消息

transaction

最大努力通知:作为第三方服务,被调用接口后,立马返回处理中等中间状态…等待调用方再次调用查询结果

事务消息

transaction

在第4和第5阶段可能会因为网络抖动发送失败,而RocketMQ在长时间没收到回馈时,会主动触发回查。

在这个时候,可以增加个事务表,记录MessageID和业务处理的状态,回查时通过回查事务表响应回查结果。

总结

模式 优缺点 并发性 即时性
LCN 支持事务回滚的中间件,如Mysql 不高
TCC 不支持事务回滚的中间件,如Mongo、Redis 不高
Seata AT 支持事务回滚的中间件,如Mysql 不高
Seata TCC 不支持事务回滚的中间件,如Mongo、Redis 不高
消息队列 依据事件表保证可靠性,吞吐量大,响应快 极高
可靠消息 研发保证消息可靠性,吞吐量大,响应快 极高
1
2
3
4
5
6
7
8
9
10
11
12
13
提高QPS的方法:
提高并发数:
1.使用多线程
2.增加连接池连接数 mysql、redis、tomcat
3.服务无状态,便于横向扩展扩机器
4.服务能力对等(打乱注册中心的url顺序)
减少响应时间:
1.异步(最终一致性)
2.缓存(减少磁盘IO,读多写少)
3.数据库优化
4.多数据分批次返回
5.减少网络IO次数
6.实时数据对接使用长连接(但会减少并发数)

最后更新: 2021年01月09日 10:39

原始链接: https://midkuro.gitee.io/2021/01/05/distributed-transaction/

× 请我吃糖~
打赏二维码