Mycat 的分片 join

1.join 概述

Join 絕對是關系型數據庫中最常用一個特性,然而在分布式環境中,跨分片的 join 確是最復雜的,最難解決一個問題。

下面我們簡單介紹下各種 Join 操作。

INNER JOIN

內連接,也叫等值連接,inner join 產生同時符合 A 表和 B 表的一組數據。

如圖:

LEFT JOIN

左連接從 A 表(左)產生一套完整的記錄,與匹配的 B 表記錄(右表) .如果沒有匹配,右側將包含 null,在 Mysql 中等同于 left outer join。

如圖:

RIGHT JOIN

同 Left join,AB 表互換即可。

CROSS JOIN

交叉連接,得到的結果是兩個表的乘積,即笛卡爾積。笛卡爾(Descartes)乘積又叫直積。假設集合A={a,b},集合 B={0,1,2},則兩個集合的笛卡爾積為{(a,0),(a,1),(a,2),(b,0),(b,1), (b,2)}。可以擴展到多個集合的情況。類似的例子有,如果 A 表示某學校學生的集合,B 表示該學校所有課程的集合,則 A 與 B 的笛卡爾積表示所有可能的選課情況。

FULL JOIN

全連接產生的所有記錄(雙方匹配記錄)在表 A 和表 B。如果沒有匹配,則對面將包含 null。

性能建議

盡量避免使用 Left join 或 Right join,而用 Inner join

在使用 Left join 或 Right join 時,ON 會優先執行,where 條件在最后執行,所以在使用過程中,條件盡可能的在 ON 語句中判斷,減少 where 的執行,少用子查詢,而用 join。

Mycat 目前版本支持跨分片的 join,主要實現的方式有四種。

全局表,ER 分片,catletT(人工智能)和 ShareJoin,ShareJoin 在開發版中支持,前面三種方式 1.3.0.1 支持

2.全局表

一個真實的業務系統中,往往存在大量的類似字典表的表格,它們與業務表之間可能有關系,這種關系,可以理解為“標簽”,而不應理解為通常的“主從關系”,這些表基本上很少變動,可以根據主鍵 ID 進行緩存,下面這張圖說明了一個典型的“標簽關系”圖:

在分片的情況下,當業務表因為規模而進行分片以后,業務表與這些附屬的字典表之間的關聯,就成了比較棘手的問題,考慮到字典表具有以下幾個特性:

? 變動不頻繁

? 數據量總體變化不大

? 數據規模不大,很少有超過數十萬條記錄。

鑒于此,MyCAT 定義了一種特殊的表,稱之為“全局表”,全局表具有以下特性:

? 全局表的插入、更新操作會實時在所有節點上執行,保持各個分片的數據一致性

? 全局表的查詢操作,只從一個節點獲取

? 全局表可以跟任何一個表進行 JOIN 操作

將字典表或者符合字典表特性的一些表定義為全局表,則從另外一個方面,很好的解決了數據 JOIN 的難題。

通過全局表+基于 E-R 關系的分片策略,MyCAT 可以滿足 80%以上的企業應用開發。

配置

全局表配置比較簡單,不用寫 Rule 規則,如下配置即可:

<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />

需要注意的是,全局表每個分片節點上都要有運行創建表的 DDL 語句

3.ER Join

MyCAT 借鑒了 NewSQL 領域的新秀 Foundation DB 的設計思路,Foundation DB 創新性的提出了 TableGroup 的概念,其將子表的存儲位置依賴于主表,并且物理上緊鄰存放,因此徹底解決了 JION 的效率和性能問題,根據這一思路,提出了基于 E-R 關系的數據分片策略,子表的記錄與所關聯的父表記錄存放在同一個數據分片上。

customer 采用 sharding-by-intfile 這個分片策略,分片在 dn1,dn2 上,orders 依賴父表進行分片,兩個表的關聯關系為 orders.customer_id=customer.id。于是數據分片和存儲的示意圖如下:

這樣一來,分片 Dn1 上的的 customer 與 Dn1 上的 orders 就可以進行局部的 JOIN 聯合,Dn2 上也如此,再合并兩個節點的數據即可完成整體的 JOIN,試想一下,每個分片上 orders 表有 100 萬條,則 10 個分片就有 1 個億,基于 E-R 映射的數據分片模式,基本上解決了 80%以上的企業應用所面臨的問題。

配置

以上述例子為例,schema.xml 中定義如下的分片配置:

<table name="customer" dataNode="dn1,dn2" rule="sharding-by-intfile">

<childTable name="orders" joinKey="customer_id" parentKey="id"/>

</table>

4.Share join

ShareJoin 是一個簡單的跨分片 Join,基于 HBT 的方式實現。

目前支持 2 個表的 join,原理就是解析 SQL 語句,拆分成單表的 SQL 語句執行,然后把各個節點的數據匯集。

配置

支持任意配置的 A,B 表如:

A,B 的 dataNode 相同:

<table name="A" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />

<table name="B" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />

A,B 的 dataNode 不同

<table name="A" dataNode="dn1,dn2 " rule="auto-sharding-long" />

<table name="B" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />


<table name="A" dataNode="dn1 " rule="auto-sharding-long" />

<table name="B" dataNode=" dn2,dn3" rule="auto-sharding-long" />

代碼測試

先把表 company 從全局表修改下配置

<table name="company" primaryKey="ID" dataNode="dn1,dn2,dn3" rule="mod-long" />

重新插入數據

下面可以看下普通的 join 和 sharejoin 的區別:

5.catlet(人工智能)

解決跨分片的 SQL JOIN 的問題,遠比想象的復雜,而且往往無法實現高效的處理,既然如此,就依靠人工的智力,去編程解決業務系統中特定幾個必須跨分片的 SQL 的 JOIN 邏輯,MyCAT 提供特定的 API 供程序員調用,這就是 MyCAT 創新性的思路——人工智能。

以一個跨節點的 SQL 為例

Select a.id,a.name,b.title from a,b where a.id=b.id

其中 a 在分片 1,2,3 上,b 在 4,5,6 上,需要把數據全部拉到本地(MyCAT 服務器),執行 JOIN 邏輯,具體過程如下(只是一種可能的執行邏輯):

EngineCtx ctx=new EngineCtx();//包含 MyCat.SQLEngine

String sql=,“ select a.id ,a.name from a ” ;

//在 a 表所在的所有分片上順序執行下面的本地 SQL

ctx.executeNativeSQLSequnceJob(allAnodes,new DirectDBJoinHandler());

DirectDBJoinHandler 類是一個回調類,負責處理 SQL 執行過程中返回的數據包,這里的這個類,主要目的是用 a 表返回的 ID 信息,去 b 表上查詢對于的記錄,做實時的關聯:

DirectDBJoinHandler{

Private HashMap rows;//Key 為 id,value 為一行記錄的 Column 原始 Byte 數組,這里是a.id,a.name,b.title 這三個要輸出的字段

Public Boolean onHeader(byte[] header){

//保存 Header 信息,用于從 Row 中獲取 Field 字段值

}

Public Boolean onRowData(byte[] rowData){

String id=getColumnAsString(“ id” );

//放入結果集,b.title 字段未知,所以先空著

rows.put(getColumnRawBytes(“ id” ),rowData);

//滿 1000 條,發送一個查詢請求

String sql=” select b.id, b.name from b where id in (………….)” ;

//此 SQL 在 B 的所有節點上并發執行,返回的結果直接輸出到客戶端

ctx.executeNativeSQLParallJob(allBNodes,sql ,new MyRowOutPutDataHandler(rows));

}

Public Boolean onRowFinished(){}

Public void onJobFinished(){

If(ctx.allJobFinished()){

{///used total time ….}

}

}

最后,增加一個 Job 事件監聽器,這里是所有 Job 完成后,往客戶端發送 RowEnd 包,結束整個流程。

ctx.setJobEventListener(new JobEventHandler(){public void onJobFinished(){ client.writeRowEndPackage()}});

以上提供一個 SQL 執行框架,完全是異步的模式執行,并且以后會提供更多高質量的 API,簡化分布式數據處理,比如內存結合文件的數據 JOIN 算法,分組算法,排序算法等等,期待更多的牛人一起來完善

6.Spark/Storm 對 join 擴展

看到這個標題,可能會感到很奇怪,Spark 和 Storm 和 Join 有關系嗎? 有必要用 Spark,storm 嗎?

mycat 后續的功能會引入 spark 和 storm 來做跨分片的 join,大致流程是這樣的在 mycat 調用 spark,storm的 api,把數據傳送到 spark,storm,在 spark,storm 進行 join,在把數據傳回 mycat,mycat 在返回給客戶端。


本文摘抄于:mycat用戶指南

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容