Skip to content

Commit 56e68a1

Browse files
committed
add readme by atomikos xa
1 parent 61ee146 commit 56e68a1

1 file changed

Lines changed: 239 additions & 0 deletions

File tree

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
# XA事务之Atomikos分布式事务框架
2+
3+
### 一.什么是XA事务?
4+
5+
##### 1.XA协议定义
6+
> - 基于一个强一致的思路,就有了基于数据库本身支持的协议,XA分布式事务。
7+
> - XA整体设计思路可以概括为,如何在现有事务模型上微调扩展,实现分布式事务。
8+
9+
##### 2.XA协议成员
10+
<img src="https://raw.githubusercontent.com/ipipman/JavaSpringBootSamples/master/ReadmeMaterial/711607681004_.pic_hd.jpg" width = "600" height = "240" alt="图片名称" align=center />
11+
> - 应用程序(Application Program ,简称 AP):用于定义事务边界(即定义事务的开始和 结束),并且在事务边界内对资源进行操作。
12+
> - 资源管理器(Resource Manager,简称 RM):如数据库、文件系统等,并提供访问资源 的方式
13+
> - 事务管理器(Transaction Manager ,简称 TM):负责分配事务唯一标识,监控事务的执行 进度,并负责事务的提交、回滚等。
14+
15+
##### 3.XA协议接口
16+
<img src="https://raw.githubusercontent.com/ipipman/JavaSpringBootSamples/master/ReadmeMaterial/721607681013_.pic_hd.jpg" width = "400" height = "450" alt="图片名称" align=center />
17+
###### 3.1XA也是2PC(两阶段)的,第一阶段(xa_start、xa_end),第二阶段(xa_prepare、xa_commit、xa_rollback)
18+
> - xa_start :负责开启或者恢复一个事务分支
19+
> - xa_end: 负责取消当前线程与事务分支的关联
20+
> - xa_prepare:询问 RM 是否准备好提交事务分支
21+
> - xa_commit:通知 RM 提交事务分支
22+
> - xa_rollback: 通知 RM 回滚事务分支
23+
> - xa_recover : 需要恢复的 XA 事务
24+
25+
##### 4.Mysql中对XA协议的支持
26+
###### 4.1 MySQL 从5.0.3开始支持InnoDB引擎的 XA 分布式事务,MySQL Connector/J 从5.0.0版本开始支持XA。(Mysql5.7以下版本XA是不安全的,如Mysql5.6断开SQL连接后,XA会自动回滚,导致无法重连补偿)
27+
<img src="https://raw.githubusercontent.com/ipipman/JavaSpringBootSamples/master/ReadmeMaterial/731607681029_.pic_hd.jpg" width = "800" height = "220" alt="图片名称" align=center />
28+
29+
###### 4.2 在 DTP 模型中,MySQL 属于资源管理器(RM)。分布式事务中存在多个 RM,由事务管理器 TM 来统一进行协调。
30+
<img src="https://raw.githubusercontent.com/ipipman/JavaSpringBootSamples/master/ReadmeMaterial/741607681043_.pic_hd.jpg" width = "800" height = "180" alt="图片名称" align=center />
31+
32+
##### 5 MySQL XA事务状态
33+
<img src="https://raw.githubusercontent.com/ipipman/JavaSpringBootSamples/master/ReadmeMaterial/751607681057_.pic_hd.jpg" width = "800" height = "500" alt="图片名称" align=center />
34+
> - SQL执行完成后进入XA_END状态
35+
> - XA_END状态可以进入XA_PREPARE状态
36+
> - XA_PREPARE状态可以进入XA_COMMIT和XA_ROLLBACK状态
37+
> - XA事务和本地事务的开启是互斥的,不能同时进行
38+
39+
##### 6 XA事务失败了怎么办?
40+
> - **业务SQL执行过程中,某个RM崩溃怎么处理?**
41+
> 如果在start和end阶段报错,还未到prepare阶段,TM会回滚全部XA事务.
42+
> - **全部prepare后,某个RM崩溃怎么处理?**
43+
> TM等待RM恢复后,重新连接,再次提交.
44+
> - **commit时,某个RM崩溃时如何处理?**
45+
> 没有好办法,通过Recover重试或人工.
46+
47+
##### 7 XA协议可能会存在的问题
48+
> - **同步阻塞问题**
49+
> 多台Mysql中Commit的速度是不一致的,在多个XA事务并发的场景下可能会存在脏数据
50+
> 如果一定要保证全局事务一致性,需要调整隔离级别为串行话(Serializable)
51+
> - **数据不一致**
52+
> 极端情况下,一定有事务失败问题,需要监控和人工处理
53+
54+
------------
55+
56+
### 二.XA框架
57+
##### 目前XA应用较为广泛的是Atomikos框架
58+
<img src="https://raw.githubusercontent.com/ipipman/JavaSpringBootSamples/master/ReadmeMaterial/761607681074_.pic_hd.jpg" width = "800" height = "320" alt="图片名称" align=center />
59+
60+
### 三.Atomikos-XA框架实战
61+
##### 1.SQL准备工作,创建两个数据库和相应的表结构
62+
```sql
63+
SET NAMES utf8mb4;
64+
SET FOREIGN_KEY_CHECKS = 0;
65+
66+
-- ----------------------------
67+
-- Table structure for demo
68+
-- ----------------------------
69+
CREATE database test;
70+
use test;
71+
DROP TABLE IF EXISTS `demo`;
72+
CREATE TABLE `demo` (
73+
`id` int NOT NULL AUTO_INCREMENT,
74+
`name` varchar(50) NOT NULL,
75+
PRIMARY KEY (`id`)
76+
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
77+
78+
SET FOREIGN_KEY_CHECKS = 1;
79+
80+
81+
CREATE database test1;
82+
use test1;
83+
DROP TABLE IF EXISTS `demo`;
84+
CREATE TABLE `demo` (
85+
`id` int NOT NULL AUTO_INCREMENT,
86+
`name` varchar(50) NOT NULL,
87+
PRIMARY KEY (`id`)
88+
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
89+
90+
SET FOREIGN_KEY_CHECKS = 1;
91+
92+
```
93+
94+
##### 2.配置AtomikosDataSourceBean数据源和JtaTransactionManager事务管理器
95+
```java
96+
@Configuration
97+
public class AtomikosDataSourceConfig {
98+
99+
@Value("${db1.datasource.url}")
100+
private String db1Url;
101+
102+
@Value("${db2.datasource.url}")
103+
private String db2Url;
104+
105+
@Value("${db1.datasource.username}")
106+
private String db1UserName;
107+
108+
@Value("${db2.datasource.username}")
109+
private String db2UserName;
110+
111+
private final static String XA_DS_CLASS_NAME = "com.mysql.cj.jdbc.MysqlXADataSource";
112+
113+
/**
114+
* 第一个数据库
115+
*
116+
* @return
117+
*/
118+
@Bean(name = "db1DataSource")
119+
@Primary
120+
@Qualifier("db1DataSource")
121+
public AtomikosDataSourceBean db1DataSourceBean() {
122+
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
123+
atomikosDataSourceBean.setUniqueResourceName("db1DataSource");
124+
atomikosDataSourceBean.setXaDataSourceClassName(XA_DS_CLASS_NAME);
125+
Properties properties = new Properties();
126+
properties.put("URL", db1Url);
127+
properties.put("user", db1UserName);
128+
properties.put("password", "");
129+
atomikosDataSourceBean.setXaProperties(properties);
130+
return atomikosDataSourceBean;
131+
}
132+
133+
/**
134+
* 第二个数据库
135+
*
136+
* @return
137+
*/
138+
@Bean(name = "db2DataSource")
139+
@Qualifier("db2DataSource")
140+
public AtomikosDataSourceBean db2DataSourceBean() {
141+
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
142+
atomikosDataSourceBean.setUniqueResourceName("db2DataSource");
143+
atomikosDataSourceBean.setXaDataSourceClassName(XA_DS_CLASS_NAME);
144+
Properties properties = new Properties();
145+
properties.put("URL", db2Url);
146+
properties.put("user", db2UserName);
147+
properties.put("password", "");
148+
atomikosDataSourceBean.setXaProperties(properties);
149+
return atomikosDataSourceBean;
150+
}
151+
152+
@Bean(destroyMethod = "close", initMethod = "init")
153+
public UserTransactionManager userTransactionManager() {
154+
UserTransactionManager userTransactionManager = new UserTransactionManager();
155+
userTransactionManager.setForceShutdown(true);
156+
return userTransactionManager;
157+
}
158+
159+
/**
160+
* 定义XA事务管理器(TM)
161+
*
162+
* @return
163+
*/
164+
@Bean(name = "XATransactionManager")
165+
public JtaTransactionManager jtaTransactionManager() {
166+
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
167+
jtaTransactionManager.setTransactionManager(userTransactionManager());
168+
return jtaTransactionManager;
169+
}
170+
}
171+
```
172+
173+
##### 3.获取Atomikos数据库连接和事务进行测试
174+
```java
175+
@Service("XATransactionService")
176+
public class XATransactionService {
177+
178+
@Autowired
179+
@Qualifier("db1DataSource")
180+
private AtomikosDataSourceBean db1DataSourceBean;
181+
182+
@Autowired
183+
@Qualifier("db2DataSource")
184+
private AtomikosDataSourceBean db2DataSourceBean;
185+
186+
/**
187+
* 测试XA事务
188+
*
189+
* @param errorSQL
190+
*/
191+
@Transactional("XATransactionManager")
192+
public void run(String errorSQL) {
193+
Connection db1Connection = null;
194+
Connection db2Connection = null;
195+
try {
196+
//test db1
197+
db1Connection = db1DataSourceBean.getConnection();
198+
String sql1 = "insert into `demo`(id, `name`) values" +
199+
"(?, ?)";
200+
PreparedStatement db1Statement = db1Connection.prepareStatement(sql1);
201+
db1Statement.setObject(1, null);
202+
db1Statement.setObject(2, "ipman");
203+
db1Statement.execute();
204+
205+
//test db2
206+
db2Connection = db2DataSourceBean.getConnection();
207+
String sql2 = "insert into `demo`(id, `name`) values" +
208+
"(?, ?)";
209+
PreparedStatement db2Statement = db2Connection.prepareStatement(
210+
Optional.ofNullable(errorSQL).orElse(sql2));
211+
db2Statement.setObject(1, null);
212+
db2Statement.setObject(2, "ipman");
213+
db2Statement.execute();
214+
215+
db1Statement.close();
216+
db2Statement.close();
217+
218+
} catch (RuntimeException | SQLException e) {
219+
throw new RuntimeException(e);
220+
} finally {
221+
try {
222+
if (db1Connection != null) {
223+
db1Connection.close();
224+
}
225+
if (db2Connection != null) {
226+
db2Connection.close();
227+
}
228+
} catch (SQLException e) {
229+
//e.printStackTrace();
230+
throw new RuntimeException(e);
231+
}
232+
}
233+
}
234+
}
235+
```
236+
237+
##### 4.运行AtomikosFrameApplication查看测试结果
238+
239+

0 commit comments

Comments
 (0)