首页 > 其他 > 详细

分布式事务,不好的事务习惯

时间:2017-01-19 12:19:35      阅读:208      评论:0      收藏:0      [点我收藏+]

分布式事务

InnoDB存储引擎支持XA事务,通过XA事务可以来支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源(transactional resources)参与一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源。全局事务要求在其中所有参与的事务要么都提交、要么都回滚,这对于事务原有的ACID要求又有了提高。另外,在使用分布式事务时,InnoDB存储引擎的事务隔离级别必须设置为SERIALIABLE。

XA事务允许不同数据库之间的分布式事务,如:一台服务器是MySQL数据库的,另一台是Oracle数据库的,又可能还有一台服务器是SQL Server数据库的,只要参与全局事务中的每个节点都支持XA事务。分布式事务可能在银行系统的转账中比较常见,如一个用户需要从上海转10 000元到北京的一个用户上:

#Bank@Shanghai:

update account set money=money-10000 where user=‘David‘;

#Bank@Beijing

Update account set money=money+10000 where user=‘Mariah‘;

这种情况一定需要分布式的事务,如果不能都提交或都回滚,在任何一个节点出现问题都会导致严重的结果:要么是David的账户被扣款,但是Mariah没收到;又或者是David的账户没有扣款,但是Mariah还是收到钱了。

分布式事务由一个或者多个资源管理器(Resource Managers)、一个事务管理器(Transaction Manager)以及一个应用程序(Application Program)组成。

资源管理器:提供访问事务资源的方法。通常一个数据库就是一个资源管理器。

事务管理器:协调参与全局事务中的各个事务。需要和参与全局事务中的所有资源管理器进行通信。

应用程序:定义事务的边界,指定全局事务中的操作。

在MySQL的分布式事务中,资源管理器就是MySQL数据库,事务管理器为连接到MySQL服务器的客户端。下图显示了一个分布式事务的模型:

技术分享

分布式事务使用两段式提交(two-phase commit)的方式。在第一个阶段,所有参与全局事务的节点都开始准备(PREPARE),告诉事务管理器它们准备好提交了。第二个阶段,事务管理器告诉资源管理器执行ROLLBACK还是COMMIT。如果任何一个节点显示不能提交,则所有的节点都被告知需要回滚。

当前Java的JTA(Java Transaction API)可以很好地支持MySQL的分布式事务,需要使用分布式事务应该认真参考其API。

下面的一个示例显示了如何使用JTA来调用MySQL的分布式事务,例子就是前面的银行转账,如下所示。

import javax.transaction.xa.Xid;

class MyXid implements Xid {
    public int formatId;
    public byte gtrid[];
    public byte bqual[];

    public MyXid() {
    }

    public MyXid(int formatId, byte gtrid[], byte bqual[]) {
        this.formatId = formatId;
        this.gtrid = gtrid;
        this.bqual = bqual;
    }

    public int getFormatId() {
        return formatId;
    }

    public byte[] getBranchQualifier() {
        return bqual;
    }

    public byte[] getGlobalTransactionId() {
        return gtrid;
    }
}


import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;

import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.sql.Connection;
import java.sql.Statement;

public class xa_demo {
    public static MysqlXADataSource GetDataSource(String connString,String user,String passwd) {
        try {
            MysqlXADataSource ds = new MysqlXADataSource();
            ds.setUrl(connString);
            ds.setUser(user);
            ds.setPassword(passwd);
            return ds;
        } catch (Exception e) {
            System.out.println(e.toString());
            return null;
        }
    }

    public static void main(String[] args) {
        String connString1 =
                "jdbc:mysql://192.168.24.43:3306/bank_shanghai";
        String connString2 =
                "jdbc:mysql://192.168.24.166:3306/bank_beijing";
        try {
            MysqlXADataSource ds1 = GetDataSource(connString1, "peter", "12345");
            MysqlXADataSource ds2 = GetDataSource(connString2, "david", "12345");
            XAConnection xaConn1 = ds1.getXAConnection();
            XAResource xaRes1 = xaConn1.getXAResource();
            Connection conn1 = xaConn1.getConnection();
            Statement stmt1 = conn1.createStatement();
            XAConnection xaConn2 = ds2.getXAConnection();
            XAResource xaRes2 = xaConn2.getXAResource();
            Connection conn2 = xaConn2.getConnection();
            Statement stmt2 = conn2.createStatement();
            Xid xid1 = new MyXid(
                    100,
                    new byte[]{0x01},
                    new byte[]{0x02});
            Xid xid2 = new MyXid(
                    100,
                    new byte[]{0x11},
                    new byte[]{0x12});
            try {
                xaRes1.start(xid1, XAResource.TMNOFLAGS);
                stmt1.execute("update account set money = money - 10000 where user = ‘david‘");
                xaRes1.end(xid1, XAResource.TMSUCCESS);
                xaRes2.start(xid2, XAResource.TMNOFLAGS);
                stmt2.execute("update account set money = money + 10000 where user = ‘mariah‘");
                xaRes2.end(xid2, XAResource.TMSUCCESS);
                int ret2 = xaRes2.prepare(xid2);
                int ret1 = xaRes1.prepare(xid1);
                if (ret1 == XAResource.XA_OK&&ret2 == XAResource.XA_OK){
                    xaRes1.commit(xid1, false);
                    xaRes2.commit(xid2, false);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            System.out.println(e.toString());
        }
    }
}

参数innodb_support_xa可以查看是否启用了XA事务支持(默认为ON):

show variables like ‘innodb_support_xa‘\G

***************************1.row***************************

Variable_name:innodb_support_xa

Value:ON

1 row in set(0.01 sec)

另外需要注意的是,对于XA事务的支持,是在MySQL体系结构的存储引擎层。因此即使不参与外部的XA事务,MySQL内部不同存储引擎层也会使用XA事务假设我们用START TRANSACTION开启了一个本地的事务,往NDB Cluster存储引擎的表t1插入一条记录,往InnoDB存储引擎的表t2插入一条记录,然后COMMIT。在MySQL内部,也是通过XA事务来协调的,这样才可以保证两张表的原子性。

不好的事务习惯

 

分布式事务,不好的事务习惯

原文:http://www.cnblogs.com/wade-luffy/p/6305900.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!