针对MyCat1.5版本,对MyCat的事务管理机制进行简要分析:
MyCat默认执行SQL不开启事务,使用事务时需要显式地开启。目前支持set autocommit = 0 、 BEGIN 、START TRANSACTION 三种方式来开启事务。我们建议使用 set autocommit = 0来开启事务。
MyCat的事务控制在会话级别,对应于MyCat源码里面的NonBlockingSession类。事务开启后,该会话所有后续的SQL执行都将进入事务,直到收到提交(commit)或者回滚(rollback)。
当MyCat收到客户端发出commit语句,表示需要执行事务的提交逻辑。当前MyCat实现的事务为弱XA事务(强XA事务还在研发当中)。
如上流程图所示,事务提交首先判断是否需要回滚,如果需要回滚,则不给用户提交。进入提交主流程,分为单节点路由和多节点路由来处理:
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);
}
}
当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命令包。
前面提到MyCat目前的分布式事务实现为弱XA事务,具有对应的优势和劣势:
较强XA事务,不会产生更多的性能消耗。
无法保证强一致性(强XA事务可以保证),跨多个节点的提交,如果前面节点提交成功,后面节点提交失败,这个时候用户必须回滚,但是只能回滚那些提交失败节点上的操作,对于已经发送了commit的节点,无法进行回滚。