Mycat源码篇 : MyCat事务管理机制分析

针对MyCat1.5版本,对MyCat的事务管理机制进行简要分析:

1. MyCat事务管理

1.1 事务开启

MyCat默认执行SQL不开启事务,使用事务时需要显式地开启。目前支持set autocommit = 0 、 BEGIN 、START TRANSACTION 三种方式来开启事务。我们建议使用 set autocommit = 0来开启事务。

MyCat的事务控制在会话级别,对应于MyCat源码里面的NonBlockingSession类。事务开启后,该会话所有后续的SQL执行都将进入事务,直到收到提交(commit)或者回滚(rollback)。

1.2 事务提交

当MyCat收到客户端发出commit语句,表示需要执行事务的提交逻辑。当前MyCat实现的事务为弱XA事务(强XA事务还在研发当中)。

1.2.1 事务提交流程分析

如上流程图所示,事务提交首先判断是否需要回滚,如果需要回滚,则不给用户提交。进入提交主流程,分为单节点路由和多节点路由来处理:

  • 对于路由到单节点的事务提交,无须考虑更多,直接发送commit命令包到对应的后端MySQL连接
    • 对于路由到多节点的事务提交,需要细分如下处理流程:
    • 如果事务内处理的表都是全局表,那么跟单节点的处理一致,不需要考虑更多,直接发送commit命令包到对应的后端MySQL连接
  • 如果事务内处理的表操作跨越多个分片,判断分布式事务是否被允许,如果不允许,中断事务并提示用户rollback;如果允许,则对于所有路由涉及到的后端连接,执行commit,在最终的OK包收到以后再通知用户commit成功。

1.2.2 事务提交代码

ServerConnection类的commit方法为事务提交主要入口,判断是否需要回滚,不需要回滚则进入处理事务提交主流程:

/**
     * 提交事务
     */
    public void commit() {
        if (txInterrupted) {
            writeErrMessage(ErrorCode.ER_YES,
                    "Transaction error, need to rollback.");
        } else {
            session.commit();
        }
    }

事务提交主流程体现在NonBlockingSession类的commit方法里:

public void commit() {
        final int initCount = target.size();
        if (initCount <= 0) {
            ByteBuffer buffer = source.allocate();
            buffer = source.writeToBuffer(OkPacket.OK, buffer);
            source.write(buffer);
            return;
        } else if (initCount == 1) {
            BackendConnection con = target.elements().nextElement();
            commitHandler.commit(con);

        } else {

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("multi node commit to send ,total " + initCount);
            }
            checkDistriTransaxAndExecute();
        }

    }

对于多节点事务提交的逻辑在checkDistriTransaxAndExecute方法里:

private void checkDistriTransaxAndExecute() {
        if(!isALLGlobal()){
            switch(MycatServer.getInstance().getConfig().getSystem().getHandleDistributedTransactions()) {
                case 1:
//                        rollback();
                    source.writeErrMessage(ErrorCode.ER_NOT_ALLOWED_COMMAND, "Distributed transaction is disabled!Please rollback!");
                    source.setTxInterrupt("Distributed transaction is disabled!");
                    break;
                case 2:
                    multiNodeCoordinator.executeBatchNodeCmd(SQLCmdConstant.COMMIT_CMD);
                    LOGGER.warn("Distributed transaction detected! Targets:" + target);
                    break;
                default:
                    multiNodeCoordinator.executeBatchNodeCmd(SQLCmdConstant.COMMIT_CMD);
            }
        } else {
            multiNodeCoordinator.executeBatchNodeCmd(SQLCmdConstant.COMMIT_CMD);
        }
    }

1.3 事务回滚

当MyCat收到客户端发出的rollback语句,表示需要执行事务的回滚逻辑。Rollback的流程处理较commit简单地多,主要逻辑为对于所有涉及到的路由节点,发送rollback命令包,并在发送完rollback命令包收到响应后,对于发送OK包给用户。回滚代码入口在ServerConnection的rollback方法里:

    /**
     * 回滚事务
     */
    public void rollback() {
        // 状态检查
        if (txInterrupted) {
            txInterrupted = false;
        }

        // 执行回滚
        session.rollback();
    }

与处理commit一样,将rollback逻辑封装到NonBlockingSession的rollback方法里面。

 public void rollback() {
        final int initCount = target.size();
        if (initCount <= 0) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("no session bound connections found ,no need send rollback cmd ");
            }
            ByteBuffer buffer = source.allocate();
            buffer = source.writeToBuffer(OkPacket.OK, buffer);
            source.write(buffer);
            return;
        }

        rollbackHandler = new RollbackNodeHandler(this);
        rollbackHandler.rollback();
    }

最后交由RollBackNodeHandler去发送rollback命令包。

2. 弱XA事务的优劣

前面提到MyCat目前的分布式事务实现为弱XA事务,具有对应的优势和劣势:

 2.1 优势

较强XA事务,不会产生更多的性能消耗。

2.2 劣势

无法保证强一致性(强XA事务可以保证),跨多个节点的提交,如果前面节点提交成功,后面节点提交失败,这个时候用户必须回滚,但是只能回滚那些提交失败节点上的操作,对于已经发送了commit的节点,无法进行回滚。
 

你可能感兴趣的:(中间件.mycat)