首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MyCat 高可用 ER 分片深度解析:父子表關聯的兩大核心場景

     編輯:MySQL 綜合教學

前言

ER(Entity-Relationship)分片是 MyCat 針對「父子表強關聯」業務設計的分片策略。核心目標:保證子表數據與其關聯的父表數據落在同一分片,從而避免跨分片 JOIN,提升查詢性能與事務一致性。本文深度剖析兩大典型場景:

  1. 父表按主鍵分片,子表關聯字段即為父表主鍵(最理想場景)
  2. 父表按非主鍵字段分片,子表仍關聯父表主鍵(需額外查找父表分片)

一、場景一:父表按主鍵分片,子表直接繼承分片規則

1.1 核心配置

schema.xml 定義 ER 關係

<table name="ORDER1" rule="mod-long">
  <childTable name="ORDER_DETAIL" joinKey="ORDER_ID" parentKey="ID" primaryKey="ID"/>
</table>
  • joinKey:子表關聯字段(外鍵)
  • parentKey:父表關聯字段(主鍵)
  • primaryKey:子表主鍵

rule.xml 分片規則

<rule>
  <columns>id</columns>
  <algorithm>mod-long</algorithm>
</rule>

1.2 建表與數據驗證

CREATE TABLE ORDER1(
  ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  SN VARCHAR(64), CREATE_TIME DATETIME
);

CREATE TABLE ORDER_DETAIL(
  ID INT AUTO_INCREMENT PRIMARY KEY,
  ORDER_ID INT, ORD_STATUS CHAR(1),
  ADDRESS VARCHAR(128), CREATE_TIME DATETIME,
  CONSTRAINT FK_ORDID FOREIGN KEY (ORDER_ID) REFERENCES ORDER1(ID)
);

寫入測試(分組寫入)

-- 北京訂單 ID=1
INSERT INTO ORDER1(ID,SN,CREATE_TIME) VALUES(1,'BJ0001',NOW());
INSERT INTO ORDER_DETAIL(ID,ORDER_ID,ORD_STATUS,ADDRESS,CREATE_TIME)
VALUES(1,1,'1','test data of ORDER1(ID=1,BJ001)',NOW());

-- 上海訂單 ID=3
INSERT INTO ORDER1(ID,SN,CREATE_TIME) VALUES(3,'SHH001',NOW());
INSERT INTO ORDER_DETAIL(ID,ORDER_ID,ORD_STATUS,ADDRESS,CREATE_TIME)
VALUES(3,3,'1','test data of ORDER1(ID=3,SHH001)',NOW());

-- 廣州訂單 ID=4
INSERT INTO ORDER1(ID,SN,CREATE_TIME) VALUES(4,'GZH004',NOW());
INSERT INTO ORDER_DETAIL(ID,ORDER_ID,ORD_STATUS,ADDRESS,CREATE_TIME)
VALUES(4,4,'1','test data of ORDER1(ID=4,GZH004)',NOW());

-- 武漢訂單 ID=6,但 ORDER_DETAIL 故意寫 ORDER_ID=4(測試是否跟隨父表)
INSERT INTO ORDER1(ID,SN,CREATE_TIME) VALUES(6,'WUHAN006',NOW());
INSERT INTO ORDER_DETAIL(ID,ORDER_ID,ORD_STATUS,ADDRESS,CREATE_TIME)
VALUES(6,4,'1','test data of ORDER1(ID=6,WUHAN005)',NOW());

1.3 數據分布驗證

-- dn21 (port 3327)
ORDER1: ID=1(BJ), ID=4(GZ)
ORDER_DETAIL: ID=1(ORDER_ID=1), ID=4(ORDER_ID=4), ID=6(ORDER_ID=4)

-- dn22 (port 3317)
ORDER1: ID=3(SH), ID=6(WUHAN)
ORDER_DETAIL: ID=3(ORDER_ID=3)

關鍵結論:ORDER_DETAIL 中 ORDER_ID=4 的三條記錄全部跟隨父表 ORDER1.ID=4 落在 dn21,即使最後一條對應的父表 ID=6 在 dn22。子表分片完全由父表主鍵決定,與子表自身主鍵無關。

1.4 日誌驗證寫入路由

關鍵日誌片段:

22:57:26.988 INSERT INTO ORDER1(ID,SN,CREATE_TIME) VALUES(3,'SHH001',NOW())
22:57:26.989 route={ 1 -> dn21{INSERT ...} }      -- 父表路由到 dn21
22:57:26.999 release connection ... attachment=dn21{...}

23:09:02.690 INSERT INTO ORDER_DETAIL ... ORDER_ID=3
23:09:02.692 route={ VALUES ... }                 -- 直接複用父表路由 dn21
23:09:02.693 release connection ... attachment=dn21{...}

子表寫入無額外路由計算,直接復用父表已確定的分片,性能極高。

1.5 JOIN 查詢路由

mysql> EXPLAIN SELECT t1.*,t2.* FROM ORDER1 t1,ORDER_DETAIL t2
       WHERE t2.ORD_STATUS='1' AND t2.ID=1 AND t1.ID=t2.ORDER_ID;
+-----------+--------------------------------------------------+
| DATA_NODE | SQL                                              |
+-----------+--------------------------------------------------+
| dn21      | SELECT ...                                       |
| dn22      | SELECT ...                                       |
+-----------+--------------------------------------------------+

注意:雖然數據同分片,但 MyCat 目前無法自動推導 JOIN 只需單分片,仍廣播至所有節點。業務層若明確知道分片,建議加上分片字段條件(如 t1.ID=1)以觸發單點路由。

二、場景二:父表按非主鍵字段分片,子表需查找父表分片

2.1 核心配置差異

父表按 PROVINCE 字符串分片

<function name="province-str-split" class="org.opencloudb.route.function.PartitionByFileMap">
  <property name="mapFile">partition-hash-str-pro.txt</property>
  <property name="type">1</property>        <!-- 字符串模式 -->
  <property name="defaultNode">0</property>
</function>

<tablerule name="tr-province">
  <rule>
    <columns>PROVINCE</columns>
    <algorithm>province-str-split</algorithm>
  </rule>
</tablerule>

映射文件 partition-hash-str-pro.txt

beijing=0
shanghai=1
tianjing=2

ER 關係定義(同場景一)

<table name="ORDER2" rule="tr-province">
  <childTable name="ORDER_DETAIL2" joinKey="ORDER_ID" parentKey="ID" primaryKey="ID"/>
</table>

2.2 寫入流程與日誌分析

插入子表時,MyCat 必須先查詢父表確定分片:

17:23:50.530 find root parent's node sql: select order2.id from order2 where order2.id=1
17:23:50.531 execute in datanode dn21
17:23:50.532 received rowResponse response,1 from ... dn21
17:23:50.732 found partition node for child table to insert dn21
17:23:50.733 route={ 1 -> dn21{INSERT INTO ORDER_DETAIL2 ...} }

額外開銷:每次子表寫入需額外發起一條「查父表主鍵」的 SQL,並在父表所在分片執行,確認分片後再寫入子表。

2.3 父表記錄不存在時的錯誤處理

mysql> INSERT INTO ORDER_DETAIL2 ... VALUES(6,9,...);
ERROR 1064 (HY000): can't find (root) parent sharding node for sql:...

日誌顯示遍歷 dn21、dn22 均未查到 ORDER2.ID=9,最終拋錯。業務必須保證先寫父表、再寫子表,且兩者在同一事務外提交(見下文避坑)。

2.4 JOIN 查詢仍為全節點掃描

mysql> EXPLAIN SELECT t1.*,t2.* FROM ORDER2 t1,ORDER_DETAIL2 t2
       WHERE t2.ORD_STATUS='1' AND t2.ID=6;
+-----------+--------------------------------------------------+
| DATA_NODE | SQL                                              |
+-----------+--------------------------------------------------+
| dn21      | SELECT ...                                       |
| dn22      | SELECT ...                                       |
+-----------+--------------------------------------------------+

三、四大常見報錯與解決方案總結

報錯現象根因解決方案
For input string: "2BJ0001" 分片字段為字符串但 type=0 (Integer) 映射函數設置 type=1
can't find (root) parent sharding node(同事務內寫父子表) 事務未提交,子表查不到父表數據 父子表分別提交事務,或使用 autocommit=1
Table 'db3.order2' doesn't exist(實際表名 ORDER2) MySQL lower_case_table_names=1 導致大小寫不敏感,MyCat 路由 SQL 使用小寫 後端 MySQL 設置 lower_case_table_names=1 並重啟,或統一表名小寫
同錯誤 3,但表已存在 MyCat 緩存舊元數據 重啟 MyCat 服務刷新元數據緩存

四、核心總結與架構建議

  1. 場景一(父表主鍵分片):子表零開銷跟隨父表,寫入性能最優,強烈推薦訂單/明細、用戶/賬單等核心業務採用。
  2. 場景二(父表非主鍵分片):子表寫入需額外查父表,增加一次網絡往返;JOIN 查詢同樣無法單分片下推。僅在「父表必須按業務屬性分片(如地域)」且無法調整為主鍵分片時採用。
  3. 事務邊界:ER 表寫入禁止在同一事務中先寫父後寫子,必須分別提交。
  4. 表名大小寫:生產環境統一設置 lower_case_table_names=1,避免路由 SQL 大小寫不匹配導致「表不存在」誤報。
  5. JOIN 優化:業務層盡量在查詢中帶上父表分片字段(如 WHERE t1.ID=?),觸發單點路由;跨分片聚合查詢建議下推至異構引擎(ClickHouse/Druid)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved