以下为个人观点,如有纰漏敬请指正。
如何设计分布式系统-CAP和BASE理论?_技术分子的博客-CSDN博客
什么是事务?
处理问题整个过程中同时具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),称为事务。
什么分布式事务?
分布式事务是涉及两个或多个网络主机的数据库事务。
XA规范
XA 是由 X/Open 组织提出的分布式事务规范,XA 规范主要定义了事务协调者(Transaction Manager)和资源管理器(Resource Manager)之间的接口。
DTP(Distributed Transaction Processing) 模型图

Xa主要规定了RM与TM之间的交互,下面来看下XA规范中定义的RM 和 TM交互的接口:

XA协议二阶段提交的一个流程示意图:

MySQL 从5.0.3开始支持XA规范,且只有InnoDB存储引擎支持。

MySQL支持XA规范后,分为内部XA事务、外部XA事务
内部XA事务
MySQL整体架构分为三层: 网络连接层, 服务层, 存储引擎层。
在向存储引擎提交数据时(存储引擎层),同时需要将提交的信息写入二进制日志(服务层),这就是一个分布式事务被称为内部XA事务。
外部XA事务
MySQL数据库外部XA可以用在分布式数据库代理层,实现对MySQL数据库的分布式事务支持,可以提供跨库的分布式事务。当然也就成了外部XA事务的协调者角色。在crash recover时控制悬挂事务是全局commit,或者rollback。
java 使用 MySQL XA 外部事务 demo
javax.transaction jta 1.1
mysql mysql-connector-java 6.0.6
public class XaDemo {public static MysqlXADataSource getDataSource(String connStr, String user, String pwd) {try {MysqlXADataSource ds = new MysqlXADataSource();ds.setUrl(connStr);ds.setUser(user);ds.setPassword(pwd);return ds;} catch (Exception e) {e.printStackTrace();}return null;}public static void main(String[] arg) {String connStr1 = "jdbc:mysql://192.168.0.1:3306/test";String connStr2 = "jdbc:mysql://192.168.0.2:3306/test";try {//从不同数据库获取数据库数据源MysqlXADataSource ds1 = getDataSource(connStr1, "root", "123456");MysqlXADataSource ds2 = getDataSource(connStr2, "root", "123456");//数据库1获取连接XAConnection xaConnection1 = ds1.getXAConnection();XAResource xaResource1 = xaConnection1.getXAResource();Connection connection1 = xaConnection1.getConnection();Statement statement1 = connection1.createStatement();//数据库2获取连接XAConnection xaConnection2 = ds2.getXAConnection();XAResource xaResource2 = xaConnection2.getXAResource();Connection connection2 = xaConnection2.getConnection();Statement statement2 = connection2.createStatement();//创建事务分支的xidXid xid1 = new MysqlXid(new byte[] { 0x01 }, new byte[] { 0x02 }, 100);Xid xid2 = new MysqlXid(new byte[] { 0x011 }, new byte[] { 0x012 }, 100);try {//事务分支1关联分支事务sql语句xaResource1.start(xid1, XAResource.TMNOFLAGS);int update1Result = statement1.executeUpdate("update account_from set money=money - 50 where id=1");xaResource1.end(xid1, XAResource.TMSUCCESS);//事务分支2关联分支事务sql语句xaResource2.start(xid2, XAResource.TMNOFLAGS);int update2Result = statement2.executeUpdate("update account_to set money= money + 50 where id=1");xaResource2.end(xid2, XAResource.TMSUCCESS);// 两阶段提交协议第一阶段int ret1 = xaResource1.prepare(xid1);int ret2 = xaResource2.prepare(xid2);// 两阶段提交协议第二阶段if (XAResource.XA_OK == ret1 && XAResource.XA_OK == ret2) {xaResource1.commit(xid1, false);xaResource2.commit(xid2, false);System.out.println("reslut1:" + update1Result + ", result2:" + update2Result);}} catch (Exception e) {e.printStackTrace();}} catch (Exception e) {e.printStackTrace();}}}
通过程序配合MySQL的XA事务功能,可能遇到的问题:
问题一、主备数据可以可能不一致性
MySQL数据库的主备数据库的同步,通过Binlog的复制完成。而Binlog是MySQL数据库内部XA事务的协调者,并且MySQL数据库为binlog做了优化——binlog不写prepare日志,只写commit日志。
所有的参与节点prepare完成,在进行xa commit前crash,crash recover如果选择commit此事务。
由于binlog在prepare阶段未写,因此主库中看来,此分布式事务最终提交了,但是此事务的操作并未写到binlog中,因此也就未能成功复制到备库,从而导致主备库数据不一致的情况出现。
在MySQL 5.5.16版本中做过测试,这个问题实际存在。crash recover之后,对xa recover返回的事务运行xa commit,对应事务提交,但是操作并未写入binlog,因此无法复制到备库。
那么是否回滚所有prepare的事务,就可以避免此问题呢?结论是仍旧不行,不仅不能解决问题一,甚至可能引起问题二。
问题二:同一事务,在各参与节点,最终状态不一致(部分提交,部分回滚)。
所有节点完成prepare,commit阶段部分已经提交 部分机器crash。crash recove 过程选择了 回滚。最终导致同一分布式事务,在各参与节点,最终状态不一致。
结论:
XA无法彻底解决分布式一致问题。
如何设计分布式系统-分布式事务-2PC、3PC?_技术分子的博客-CSDN博客
参考:
分布式事务
XA规范
XA模型