一、概述
最近开始学习Mycat,希望用它来实现多种数据库的分片(Sharding)。但当数据库为PostgreSQL时,却发现二级子表始终无法数据分片。经分析Mycat源码及简单测试,基本确定Mycat目前版本尚不支持非Mysql的二级子表分片。
按Mycat的解释,二级子表是指父表的父表不为空的表。
二、问题重现
测试环境为:Mycat 1.6.6.1,逻辑库为MySql5.7,两个PostgreSQL 10.6节点。
有三个表组成E-R分片:父表PERSON(主键ID),子表CARD(主键ID,外键PERSON_ID),二级子表CRAD_ITEM(主键CID,外键CID)。
具体配置见schema.xml:
<?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100"> <table name=PERSON" primaryKey="ID" dataNode="dn1,dn2" rule="mod-long"> <childTable name="CARD" primaryKey="ID" joinKey="PERSON_ID" parentKey="ID"> <childTable name="CARD_ITEM" joinKey="CID" parentKey="ID" /> </childTable> </table> </schema> <dataNode name="dn1" dataHost="MyCat_PG_1" database="shard1" /> <dataNode name="dn2" dataHost="MyCat_PG_2" database="shard2" /> <dataHost name="MyCat_PG_1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="postgresql" dbDriver="jdbc" switchType="1" slaveThreshold="100"> <heartbeat>select 1</heartbeat> <writeHost host="hostM1" url="jdbc:postgresql://192.168.64.185:5432/shard1" user="shard1" password="123456"> </writeHost> </dataHost> <dataHost name="MyCat_PG_2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="postgresql" dbDriver="jdbc" switchType="1" slaveThreshold="100"> <heartbeat>select 1</heartbeat> <writeHost host="hostM2" url="jdbc:postgresql://192.168.64.186:5432/shard2" user="shard2" password="123456"> </writeHost> </dataHost> </mycat:schema>
测试过程中,向父表PERSON插入数据成功,向子表CARD插入数据也成功,但向二级子表CARD_ITEM插入时报1064错误:
mysql> insert into person(id, num) values(5, '5555555'); Query OK, 1 row affected (0.14 sec) OK! mysql> insert into card(id, person_id) values(52, 5); Query OK, 1 row affected (0.03 sec) OK! mysql> insert into card_item(cid, data) values(52, 'It is 52'); ERROR 1064 (HY000): can't find (root) parent sharding node for sql:insert into card_item(cid, data) values(52, 'It is 52')
三、分析
尝试过不同配置几次,始终报这个错误。无奈下只好去研究源码,报错的地方是io.mycat.route.util.RouterUtil的方法processERChildTable()。
在该方法中,如果要向二级子表中插入记录,要判别对应的父表(根节点)在哪个dataNode,该语句是:
final String findRootTBSql = tc.getLocateRTableKeySql().toLowerCase() + joinKeyVal;
跟踪到该处时,变量findRootTBSql值为:
select `person`.id from `card`,`person` where `person`.id=`card`.person_id and `card`.id=52;
到这里已基本知道原因,是Mycat将每个表名都以“`”引起来了。这种语法只有mysql支持,而PostgreSQL和Oracle均不支持,所以报错。
进一步的尝试是修改该方法,将变量findRootTBSql中的“`”强行去掉,但之后插入二级子表时仍然报错,查看mycat.log,错误信息是:
2018-11-21 10:31:07.509 WARN [BusinessExecutor2] (io.mycat.backend.mysql.nio.handler.FetchStoreNodeOfChildTableHandler.executeException(FetchStoreNodeOfChildTableHandler.java:268)) - executeException
java.io.UnsupportedEncodingException: unsupported yet
再一直跟踪,错误是在类io.mycat.backend.jdbc.JDBCConnection的query(String)方法抛出的,估计开发人员也认为目前不能解决。代码如下:
@Override public void query(final String sql) throws UnsupportedEncodingException { if(respHandler instanceof ConnectionHeartBeatHandler) { justForHeartbeat(sql); } else { throw new UnsupportedEncodingException("unsupported yet "); } }
只好期待MyCat以后的版本来解决了。
四、结论
目前的MyCat版本(1.6.6.1),在以JDBC连接非MySql数据库(比如PostgreSQL)时,不支持二级子表的分片。