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用戶指南