在做读写分离时,我们采用了以下方案,在dataHost上配置了balance=0, 默认禁用读写分离。然后只有带有/*#mycat:db_type=slave*/这样hint的语句才会被分发到readHost,后续会逐渐通过添加hint到新的hint语句来来放开更多的流量,希望这样逐步放开读流量可以避免一次性全部迁移带来的影响。
<dataHost name="masterFirst" maxCon="100" minCon="50" balance="0" tempReadHostAvailable="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="-1" slaveThreshold="10">
<writeHost host=mysqla>
<readHost host=mysqlb/>
</writeHost>
....
但是测试发现部分事务会丢失记录,通过分析发现部分经过MyCAT的事务会被一条rollback回滚。经过分析,是以下模式的事务会被回滚:
set autocommit=0;
INSERT INTO tablea ...
/*#mycat:db_type=slave*/SELECT xx from tableb
COMMIT
在MySQL端看到以上序列会变为以下模式,在writeHost上
set autocommit=0;INSERT INTO tablea ...
ROLLBACK
在readHost上
SELECT xx from tableb
这样,插入到表tablea的数据就丢了。而此时,业务服务器,MyCAT服务器和MySQL端都无任何错误日志。
经过分析,发现是MyCAT对以上场景的处理逻辑如下
- MyCAT在一个连接到MySQL writeHost的连接内碰到一个到readHost的语句
- MyCAT释放writeHost连接回连接池,而释放连接会发起rollback(这是为什么会多一个rollback的原因)
- MyCAT新建到readHost的连接,并执行到readHost的语句
这个逻辑应该是没有问题的,因为MyCAT要保证在一个前端事务边界内(面向应用)任一时刻只能关联同一个后端连接(到MySQL)。而根源是应用程序做读写分离用法上的问题。但是NonBlockingSession.java代码在释放并切换连接只产生了一行debug日志,导致我们在丢失数据的时候在所有地方都找不到错误日志。所以debug级别明显不利于排查。已经在MyCAT GitHub提醒进行改正。
总结MyCAT的前后端连接管理策略。
- MyCAT以事务为单元来请求和使用后端MySQL连接池
- 开始事务即可从MySQL连接池中拿连接(后端连接),关闭事务即释放连接到后端连接池
- 释放MySQL连接回连接池前会在MySQL连接上发一个rollback指令。这会使在应用发一个commit时,经过MyCAT,MySQL收到commit和rollback两条语句。而应用发一个rollback,经过MyCAT,MySQL会收到rollback和rollback两条语句
- 一个后端MySQL连接上出现事务内的SQL错误(比如主键冲突)后,应用如果不进行rollback就在别的线程复用前端MyCAT连接,那么应用新的SQL全部都会失败。
- 如果MySQL连接所在事务处于异常状态,前端关闭连接,对应MySQL连接会被关闭
本文介绍了读写分离方案,在dataHost上配置禁用读写分离,通过hint语句分发读流量。测试发现部分MyCAT事务会丢失记录,经分析是MyCAT处理逻辑与应用程序用法问题导致,且debug日志不利于排查。还总结了MyCAT前后端连接管理策略。
6879

被折叠的 条评论
为什么被折叠?



