MySQL XA可应用于两种场景
1、同一实例下的不同引擎。
2、不同实例下的数据库。
现在几乎都是用 InnoDB、所以此文主要讲第二种场景。不同实例下的数据库实现分布式事务。
一、MySQL XA语句
1、XA START ‘xid’
开启全局事务,将状态置为active
xid 全局唯一
2、XA END ‘xid’
将事务状态置为IDLE
确定DML语句结束
START/END 也标明参与全局事务的语句边界
3、XA PREPARE ‘xid’
将事务状态置为PREPARED
对于成功的事务,可以通过 XA RECOVER 查询。
4、XA COMMIT ‘xid’
将事务提交
5、XA ROLLBACK ‘xid’
将事务回滚
二、MySQL XA 三个重要的节点
1、AP 应用程序
2、TM 事务管理器 --事务协调整者,根据RM反馈的状态,决定提交,回滚等操作。
3、RM 资源管理器 --通常就是指MySQL 库本身
AP、TM、RM 图
三、程序实例
下面我用C#写了一段控制台充当TM
C#控制台实例
using System;
using MySql.Data.MySqlClient;
namespace TestMysqlXA
{
internal class Program
{
static string connstr1 = "server=192.168.1.1;port=3521;user=dev;database=DEV;password=1232";
static string connstr2 = "server=192.168.1.2;port=3306;user=test;database=test;password=123";
static void Main(string[] args)
{
using (MySqlConnection conn1 = new MySqlConnection(connstr1))
using (MySqlCommand com1 = conn1.CreateCommand())
using (MySqlConnection conn2 = new MySqlConnection(connstr2))
using (MySqlCommand com2 = conn2.CreateCommand())
{
conn1.Open();
conn2.Open();
string sql = "XA START '3f68419a-cde3-11ec-a6e1-0c42a1b776e7'";
com1.CommandText = sql;
com2.CommandText = sql;
com1.ExecuteNonQuery();
com2.ExecuteNonQuery();
try
{
com1.CommandText = "insert into multi_dbtrans values(3, '商品3');";
com1.ExecuteNonQuery();
com1.CommandText = "XA END '3f68419a-cde3-11ec-a6e1-0c42a1b776e7'";
com1.ExecuteNonQuery();
com2.CommandText = "update multi_dbtrans set goodsname = '111' where goodsid = 1 ;";
com2.ExecuteNonQuery();
com2.CommandText = "insert into multi_dbtrans values(3, '商品3');";
com2.ExecuteNonQuery();
com2.CommandText = "XA END '3f68419a-cde3-11ec-a6e1-0c42a1b776e7'";
com2.ExecuteNonQuery();
com1.CommandText = "XA PREPARE '3f68419a-cde3-11ec-a6e1-0c42a1b776e7'";
com1.ExecuteNonQuery();
com2.CommandText = "XA PREPARE '3f68419a-cde3-11ec-a6e1-0c42a1b776e7'";
com2.ExecuteNonQuery();
sql = "XA COMMIT '3f68419a-cde3-11ec-a6e1-0c42a1b776e7'";
com1.CommandText = sql;
com2.CommandText = sql;
com1.ExecuteNonQuery();
com2.ExecuteNonQuery();
}
catch (Exception ex)
{
sql = "XA ROLLBACK '3f68419a-cde3-11ec-a6e1-0c42a1b776e7'";
com1.CommandText = sql;
com2.CommandText = sql;
com1.ExecuteNonQuery();
com2.ExecuteNonQuery();
}
Console.ReadLine();
}
}
}
}
复制
四、异常处理
1、构建测试数据
create table if not exists multi_dbtrans
(
goodsid int not null comment '主键ID',
goodsname varchar(50) not null comment '商品名称',
primary key pk_goodsid(goodsid)
)engine = innodb comment '测试表';
复制
插入测试数据
insert into multi_dbtrans
values(1,'商品1'),(3,'商品3');
复制
2、在DML语句处有RM出现异常
xa start '111222';
update multi_dbtrans set goodsname = '111' where goodsid = 1 ;
insert into multi_dbtrans -- 此语句会报错,因为已有goodsid = 3 的了。
values(3,'商品3');
复制
这时可以看到已有锁了。
所以如第二图所示,在DML语句时,就已加锁了。
但对于没有 XA PREPARE 的事务
用XA RECOVER; 是查询不到的
那该怎么查询呢。
可通过下列语句查询
SELECT parent.xid_gtrid,child.*
FROM performance_schema.events_transactions_current AS parent
INNER JOIN performance_schema.data_locks AS child
WHERE
parent.THREAD_ID = child.THREAD_ID
AND parent.EVENT_ID < child.EVENT_ID
AND (
child.EVENT_ID <= parent.END_EVENT_ID
OR parent.END_EVENT_ID IS NULL
);
复制
通过上面语句,可以看到这个xid.
但是对于 没有 XA END 的事务来说,是不能rollback或commit的
那怎么才能释放资源呢。
有两种方法。
第一种。断开连接
只要连接断开。该事务就自动回滚了。所有资源就释放掉了
第二种。 kill掉
通过data_locks 可以看到 THREAD_ID
select * from performance_schema.data_locks;
复制
再通过threads表找到PROCESSLIST_ID
select * from performance_schema.threads WHERE THREAD_ID = 94
复制
还可以通过 events_transactions_current 确定xid是不是一致的。
select * from performance_schema.events_transactions_current where thread_id = 141
复制
通过 events_statements_history 表可以找到 该事务包含的语句。
SELECT * FROM performance_schema.events_statements_history WHERE thread_id=141
复制
找到PROCESSLIST_ID
然后就可以kill了
kill 50
复制
Kill 后 锁资源等都释放了。
2、在XA PREPARE语句处有RM出现异常
对于未成功XA PREPARE 的事务,可以采用三种方式释放事务
1、连接断开自动回滚
2、kill pid 回滚
3、手动 XA ROLLBACK 回滚。
对于成功XA PREPARE 的事务
只能通过 XA ROLLBACK 回滚。或 XA COMMIT提交
对于已成功XA PREPARE 的事务。连接断开,服务重启都不会自动回滚或提交。必须手动处理
已成功 XA PREPARE 的事务可通过 XA RECOVER 直接查看。
XA RECOVER;
复制
此时可以手动执行 XA ROLLBACK ‘111222’ 或 XA COMMIT ‘111222’
2、在XA COMMIT语句处有RM出现异常
对于已成功 XA COMMIT 的事务,数据库RM已完成了持久化,不能回滚。如果需要回滚需要TM反向操作自己实现回滚。
对于未成功XA COMMIT 的事务 参见上点。处理方式。
好的。MySQL XA整体的介始就到此了。
最后做一个总结吧
总结
1、MySQL XA 并不是强一致性的分布式事务。因为最后一步,有可能有些RM已Commit 而有的RM 由于网络等原因未收到Commit命令。
这就需要TM 根据业务需要来决定是 COMMIT还是ROLLBACK. 达到最终一致性。
2、事务锁资源时间较长,对性能有影响,当第一个RM 执行DML的时候,第一个RM就已上锁完成。 需要等到所有RM提交才释放锁。所以对于第一个RM锁的时候更长,实际中当然可以参照2PL 特性,将资源争抢最历害的RM放在最后。
3、没有全局的ReadView,因为MySQL的ReadView 是实例级别的。 .对一致性读要求较高的业务,肯定不适合MySQL XA。要么就需要TM来实现全局的ReadView.
评论
