使用Mycat用于读写分离时,对数据库的事务不跨库,如果硬要打开事务没有关闭的话,容易产生系统事务紊乱等问题。通过源代码仔细分析,发现mycat并非透传sql语句。
start transaction的处理
public final class StartHandler {
private static final byte[] AC_OFF = new byte[] { 7, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0 };
public static void handle(String stmt, ServerConnection c, int offset) {
switch (ServerParseStart.parse(stmt, offset)) {
case ServerParseStart.TRANSACTION:
if (c.isAutocommit())
{
c.setAutocommit(false);
c.write(c.writeToBuffer(AC_OFF, c.allocate()));
}else
{
c.getSession2().commit() ;
}
break;
default:
c.execute(stmt, ServerParse.START);
}
}
}
也就是说,mycat把start transaction 改成了set commit=0,而且不是立即发送给后端mysql
2019-11-19T01:43:01.970695Z 5 Query SET autocommit=0;
2019-11-19T01:43:01.970695Z 5 Query update city set Name='MJMJ' where id=1
2019-11-19T01:43:06.907978Z 5 Query commit
2019-11-19T01:43:06.953980Z 5 Query rollback
2019-11-19T01:43:19.846718Z 5 Query SET autocommit=1;
2019-11-19T01:43:19.846718Z 5 Query select * from city where id = 1
前两条是一起送给mysql的。如果事务没有关闭,导致整个连接的autocommit属性的改变。
多了一个rollback
从上面一个简单的事务,可以发现每次commit后面都会跟着一个rollback
通过分析源代码NoBlockingSession.java发现
public void releaseConnection(RouteResultsetNode rrn, boolean debug, final boolean needRollback) {
BackendConnection c = target.remove(rrn);
if (c != null) {
if (debug) {
LOGGER.debug("release connection " + c);
}
if (c.getAttachment() != null) {
c.setAttachment(null);
}
if (!c.isClosedOrQuit()) {
if (c.isAutocommit()) {
c.release();
} //else if (needRollback) {
c.setResponseHandler(new RollbackReleaseHandler());
c.rollback();
} //else {
c.release();
}
}
}
}
needRollback的判断竟然被注释掉了,实在令人费解啊!
至此,mycat单node简单事务问题终于研究清楚了。