一、概述
最近開始學習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)時,不支持二級子表的分片。