社區投稿 | DBLE和Mycat跨分片查詢結果不一致案例分析

1.背景

某一零售業後端使用了分佈式中間件+MySQL數據庫作爲後端存儲。但是因爲歷史問題存在兩種分佈式中間件,分別是Mycat和DBLE,共用一組後端MySQL實例。分片規則以及後端數據完全一致。最近碰到了一個比較有意思的場景,財務結算單來往明細和業務來往單據的關聯查詢。一條跨節點join查詢在DBLE、Mycat的查詢得到的結果不一致。究竟誰對誰錯?

2.DBLE項目介紹

DBLE官方網站:
https://opensource.actionsky.com
可以詳細瞭解DBLE的背景和應用場景,本文不涉及到的細節都可在官方文檔獲得更細節都信息;對於剛瞭解到同學,可以以本文爲快速入門基礎

DBLE 官方項目:
https://github.com/actiontech/dble
如對源碼有興趣或者需要定製的功能的可以通過源碼編譯安裝

DBLE 下載地址:
https://github.com/actiontech/dble/releases

DBLE 官方社區交流:669663113

3.環境準備

在虛擬機搭建類似架構,模擬場景,比較Mycat-DBLE在跨節點join上的異同點。

3.1測試架構

測試環境架構比較簡單,DBLE與Mycat共用數據庫。

社區投稿 | DBLE和Mycat跨分片查詢結果不一致案例分析

3.2測試軟件版本
軟件名稱 軟件版本 端口 管理端口
DBLE DBLE-2.18.10.1-cb392c3-20181106093917 3309 3310
Mycat Mycat-1.6-RELEASE-20161028204710 8066 9066
MySQL 5.7.21-log 3306
3.3表結構

結算單來往明細表

CREATE TABLE `t_bl_detail` (
  `unit_num` int(11) DEFAULT NULL,
  `tenantid` int(11) DEFAULT NULL,
  `detail_num` int(11) DEFAULT NULL,
  `balance_date` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

業務來往單據表

CREATE TABLE `t_bl_super_detail` (
  `unit_num` int(11) DEFAULT NULL,
  `sup_id` int(11) DEFAULT NULL,
  `tenantid` int(11) DEFAULT NULL,
  `bl_unit_num` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3.4分片規則配置

配置表t_bl_detail、t_bl_super_detail,使用取模算法,數據分佈在db1-db4四個database中。

3.41schema配置

DBLE:

   <schema name="testdb" sqlMaxLimit="100" dataNode="dn01">
        <table name="t_bl_detail" rule="mod4Series" dataNode="dn01,dn02,dn03,dn04"></table>
        <table name="t_bl_super_detail" rule="mod4Series" dataNode="dn01,dn02,dn03,dn04"></table>
    </schema>
    <dataNode name="dn01" dataHost="group1" database="db1"></dataNode>
    <dataNode name="dn02" dataHost="group2" database="db2"></dataNode>
    <dataNode name="dn03" dataHost="group1" database="db3"></dataNode>
    <dataNode name="dn04" dataHost="group2" database="db4"></dataNode>
    <dataHost name="group1" maxCon="100" minCon="5" balance="0" switchType="-1">
        <heartbeat>select user()</heartbeat>
        <writeHost host="mysql-w9zhkr" url="1.1.1.26:3306" user="ushard" password="VRDkwsUlq++O3HeamsDcuW/2K22si3RIhcTpAdjalbkiinNAeUUDWk11ttls1Z3PXY5TxGJToeK4LsJE3+k0Wg==" id="mysql-w9zhkr" usingDecrypt="1"></writeHost>
    </dataHost>
    <dataHost name="group2" maxCon="100" minCon="5" balance="0" switchType="-1">
        <heartbeat>select user()</heartbeat>
        <writeHost host="mysql-ci2va0" url="1.1.1.27:3306" user="ushard" password="O/iCi0F+9ts5YR78ZcQnKzpb5GqG7xSUzJcO44yZ3BhNkT5E7aiZakmz1tbKIQylnqh20vu5Z9egXBUC3GOKow==" id="mysql-ci2va0" usingDecrypt="1"></writeHost>
    </dataHost>

Mycat:

    <schema name="testdb" sqlMaxLimit="100" dataNode="dn01">
        <table name="t1" rule="mod4Series" dataNode="dn01,dn02,dn03,dn04"></table>
        <table name="t2" rule="mod4Series" dataNode="dn01,dn02,dn03,dn04"></table>
    </schema>
    <dataNode name="dn01" dataHost="group1" database="db1"></dataNode>
    <dataNode name="dn02" dataHost="group2" database="db2"></dataNode>
    <dataNode name="dn03" dataHost="group1" database="db3"></dataNode>
    <dataNode name="dn04" dataHost="group2" database="db4"></dataNode>
    <dataHost name="group1" maxCon="100" minCon="5" balance="0" switchType="-1" dbType="mysql" dbDriver="native">
        <heartbeat>select user()</heartbeat>
        <writeHost host="mysql-w9zhkr" url="1.1.1.26:3306" user="ushard" password="ushard"></writeHost>
    </dataHost>
    <dataHost name="group2" maxCon="100" minCon="5" balance="0" switchType="-1" dbType="mysql" dbDriver="native">
        <heartbeat>select user()</heartbeat>
        <writeHost host="mysql-ci2va0" url="1.1.1.27:3306" user="ushard" password="ushard"></writeHost>
    </dataHost>
3.42rule配置

DBLE:

    <tableRule name="mod4Series">
        <rule>
            <columns>unit_num</columns>
            <algorithm>mod4DB</algorithm>
        </rule>
    </tableRule>
    <function name="mod4DB" class="Hash">
        <property name="partitionCount">4</property>
        <property name="partitionLength">1</property>
    </function>

Mycat:

    <tableRule name="mod4Series">
        <rule>
            <columns>id</columns>
            <algorithm>mod4DB</algorithm>
        </rule>
    </tableRule>
    <function name="mod4DB" class="io.mycat.route.function.PartitionByMod">
        <property name="count">4</property>
    </function>

4.比對開始

4.1準備測試數據

隨便登錄哪一臺中間件寫入測試數據

insert into t_bl_detail values(1,3,123443,'2019-01-01 00:00:00');
insert into t_bl_detail values(2,3,3423524,'2019-01-01 00:00:00');
insert into t_bl_detail values(3,3,245245,'2019-01-01 00:00:00');
insert into t_bl_detail values(4,4,356356,'2019-01-01 00:00:00');

insert into t_bl_super_detail values(1,10342,3,2);
insert into t_bl_super_detail values(2,12355,3,2);
insert into t_bl_super_detail values(3,62542,3,3);
insert into t_bl_super_detail values(4,74235,4,1);

社區投稿 | DBLE和Mycat跨分片查詢結果不一致案例分析

4.2執行跨節點join查詢
select
 m.unit_num,
 n.sup_id,
 n.tenantid,
 m.detail_num,
 n.bl_unit_num,
 m.balance_date
from
 t_bl_detail m
join t_bl_super_detail n on
 m.tenantid = n.tenantid
where
 n.tenantid = 3
 and m.unit_num = n.bl_unit_num;

在通過中間件之前,現在MySQL中執行一遍看下結果,作爲預期結果供後續案例使用。

社區投稿 | DBLE和Mycat跨分片查詢結果不一致案例分析

分別通過DBLE、mycat執行跨節點join語句

社區投稿 | DBLE和Mycat跨分片查詢結果不一致案例分析
社區投稿 | DBLE和Mycat跨分片查詢結果不一致案例分析

可看到相同的查詢語句,DBLE執行結果符合預期,Mycat執行結果缺失。數據差異在於DBLE查詢結果相較於Mycat多了跨節點的結果。雖然Mycat執行跨節點join不報錯,但是查詢結果卻和預期不一致。

4.3執行計劃

只從結果上判斷並沒有辦法知道是什麼原因導致了Mycat結果缺失,查看查詢計劃,比較兩者差異。

4.31DBLE

通過DBLE的執行計劃可看出,DBLE內部分別對結算明細表、業務單據表做了各自的數據查詢,將查詢結果在中間層做了merge。最後獲得跨節點join的結果

  • DBLE的執行計劃
    mysql> explain select m.unit_num,n.sup_id,n.tenantid,m.detail_num,n.bl_unit_num,m.balance_date from t_bl_detail m join t_bl_super_detail n on m.tenantid=n.tenantid where n.tenantid = 3 and m.unit_num=n.bl_unit_num;
    +-----------------+---------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | DATA_NODE       | TYPE          | SQL/REF                                                                                                                                                              |
    +-----------------+---------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | dn01_0          | BASE SQL      | select `m`.`unit_num`,`m`.`detail_num`,`m`.`balance_date`,`m`.`tenantid` from  `t_bl_detail` `m` where m.tenantid = 3 ORDER BY `m`.`tenantid` ASC,`m`.`unit_num` ASC |
    | dn02_0          | BASE SQL      | select `m`.`unit_num`,`m`.`detail_num`,`m`.`balance_date`,`m`.`tenantid` from  `t_bl_detail` `m` where m.tenantid = 3 ORDER BY `m`.`tenantid` ASC,`m`.`unit_num` ASC |
    | dn03_0          | BASE SQL      | select `m`.`unit_num`,`m`.`detail_num`,`m`.`balance_date`,`m`.`tenantid` from  `t_bl_detail` `m` where m.tenantid = 3 ORDER BY `m`.`tenantid` ASC,`m`.`unit_num` ASC |
    | dn04_0          | BASE SQL      | select `m`.`unit_num`,`m`.`detail_num`,`m`.`balance_date`,`m`.`tenantid` from  `t_bl_detail` `m` where m.tenantid = 3 ORDER BY `m`.`tenantid` ASC,`m`.`unit_num` ASC |
    | merge_1         | MERGE         | dn01_0; dn02_0; dn03_0; dn04_0                                                                                                                                       |
    | shuffle_field_1 | SHUFFLE_FIELD | merge_1                                                                                                                                                              |
    | dn01_1          | BASE SQL      | select `n`.`sup_id`,`n`.`tenantid`,`n`.`bl_unit_num` from  `t_bl_super_detail` `n` where n.tenantid = 3 ORDER BY `n`.`tenantid` ASC,`n`.`bl_unit_num` ASC            |
    | dn02_1          | BASE SQL      | select `n`.`sup_id`,`n`.`tenantid`,`n`.`bl_unit_num` from  `t_bl_super_detail` `n` where n.tenantid = 3 ORDER BY `n`.`tenantid` ASC,`n`.`bl_unit_num` ASC            |
    | dn03_1          | BASE SQL      | select `n`.`sup_id`,`n`.`tenantid`,`n`.`bl_unit_num` from  `t_bl_super_detail` `n` where n.tenantid = 3 ORDER BY `n`.`tenantid` ASC,`n`.`bl_unit_num` ASC            |
    | dn04_1          | BASE SQL      | select `n`.`sup_id`,`n`.`tenantid`,`n`.`bl_unit_num` from  `t_bl_super_detail` `n` where n.tenantid = 3 ORDER BY `n`.`tenantid` ASC,`n`.`bl_unit_num` ASC            |
    | merge_2         | MERGE         | dn01_1; dn02_1; dn03_1; dn04_1                                                                                                                                       |
    | shuffle_field_3 | SHUFFLE_FIELD | merge_2                                                                                                                                                              |
    | join_1          | JOIN          | shuffle_field_1; shuffle_field_3                                                                                                                                     |
    | shuffle_field_2 | SHUFFLE_FIELD | join_1                                                                                                                                                               |
    +-----------------+---------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    14 rows in set (0.00 sec)
4.32Mycat

Mycat對於跨節點join的處理則相對暴力,直接將查詢語句下發到各個節點,最後將結果進行彙總,如果表連接涉及到跨節點。則跨節點的數據無法進行join。

  • Mycat的執行計劃
    mysql> explain select m.unit_num,n.sup_id,n.tenantid,m.detail_num,n.bl_unit_num,m.balance_date from t_bl_detail m join t_bl_super_detail n on m.tenantid=n.tenantid where n.tenantid = 3 and m.unit_num=n.bl_unit_num;
    +-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | DATA_NODE | SQL                                                                                                                                                                                                    |
    +-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | dn01      | select m.unit_num,n.sup_id,n.tenantid,m.detail_num,n.bl_unit_num,m.balance_date from t_bl_detail m join t_bl_super_detail n on m.tenantid=n.tenantid where n.tenantid = 3 and m.unit_num=n.bl_unit_num |
    | dn02      | select m.unit_num,n.sup_id,n.tenantid,m.detail_num,n.bl_unit_num,m.balance_date from t_bl_detail m join t_bl_super_detail n on m.tenantid=n.tenantid where n.tenantid = 3 and m.unit_num=n.bl_unit_num |
    | dn03      | select m.unit_num,n.sup_id,n.tenantid,m.detail_num,n.bl_unit_num,m.balance_date from t_bl_detail m join t_bl_super_detail n on m.tenantid=n.tenantid where n.tenantid = 3 and m.unit_num=n.bl_unit_num |
    | dn04      | select m.unit_num,n.sup_id,n.tenantid,m.detail_num,n.bl_unit_num,m.balance_date from t_bl_detail m join t_bl_super_detail n on m.tenantid=n.tenantid where n.tenantid = 3 and m.unit_num=n.bl_unit_num |
    +-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    4 rows in set (0.00 sec)

5.總結

Mycat是一款非常優秀的分佈式中間件,但是在某些細節方面處理的不盡人意。在跨節點關聯查詢場景下,Mycat採取的策略是直接將語句透傳到各個節點上,將獲取到的結果整合後返回,得到的結果集和預期結果有出入,缺失了跨節點關聯的數據。

DBLE處理跨節點的關聯查詢是先獲取到關聯需要的數據,提取到中間件進行融合,得到關聯查詢的結果並返回。得到的結果集符合預期,與MySQL執行結果一致。可見DBLE在跨節點關聯查詢方面做了優化,能夠提供準確的查詢結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章