1.join 概述
Join 絕對是關(guān)系型數(shù)據(jù)庫中最常用一個特性硫朦,然而在分布式環(huán)境中,跨分片的 join 確是最復(fù)雜的仅叫,最難解決一個問題。
下面我們簡單介紹下各種 Join 操作挠日。
INNER JOIN
內(nèi)連接缘眶,也叫等值連接,inner join 產(chǎn)生同時符合 A 表和 B 表的一組數(shù)據(jù)束莫。
如圖:
LEFT JOIN
左連接從 A 表(左)產(chǎn)生一套完整的記錄,與匹配的 B 表記錄(右表) .如果沒有匹配,右側(cè)將包含 null,在 Mysql 中等同于 left outer join懒棉。
如圖:
RIGHT JOIN
同 Left join,AB 表互換即可草描。
CROSS JOIN
交叉連接,得到的結(jié)果是兩個表的乘積策严,即笛卡爾積穗慕。笛卡爾(Descartes)乘積又叫直積。假設(shè)集合A={a,b}妻导,集合 B={0,1,2}逛绵,則兩個集合的笛卡爾積為{(a,0),(a,1),(a,2),(b,0),(b,1), (b,2)}【缶拢可以擴展到多個集合的情況术浪。類似的例子有,如果 A 表示某學(xué)校學(xué)生的集合狐肢,B 表示該學(xué)校所有課程的集合添吗,則 A 與 B 的笛卡爾積表示所有可能的選課情況沥曹。
FULL JOIN
全連接產(chǎn)生的所有記錄(雙方匹配記錄)在表 A 和表 B份名。如果沒有匹配,則對面將包含 null。
性能建議
盡量避免使用 Left join 或 Right join,而用 Inner join
在使用 Left join 或 Right join 時妓美,ON 會優(yōu)先執(zhí)行僵腺,where 條件在最后執(zhí)行,所以在使用過程中壶栋,條件盡可能的在 ON 語句中判斷辰如,減少 where 的執(zhí)行,少用子查詢,而用 join贵试。
Mycat 目前版本支持跨分片的 join,主要實現(xiàn)的方式有四種琉兜。
全局表,ER 分片毙玻,catletT(人工智能)和 ShareJoin豌蟋,ShareJoin 在開發(fā)版中支持,前面三種方式 1.3.0.1 支持
2.全局表
一個真實的業(yè)務(wù)系統(tǒng)中桑滩,往往存在大量的類似字典表的表格梧疲,它們與業(yè)務(wù)表之間可能有關(guān)系,這種關(guān)系运准,可以理解為“標(biāo)簽”幌氮,而不應(yīng)理解為通常的“主從關(guān)系”,這些表基本上很少變動胁澳,可以根據(jù)主鍵 ID 進行緩存该互,下面這張圖說明了一個典型的“標(biāo)簽關(guān)系”圖:
在分片的情況下,當(dāng)業(yè)務(wù)表因為規(guī)模而進行分片以后韭畸,業(yè)務(wù)表與這些附屬的字典表之間的關(guān)聯(lián)慢洋,就成了比較棘手的問題塘雳,考慮到字典表具有以下幾個特性:
? 變動不頻繁
? 數(shù)據(jù)量總體變化不大
? 數(shù)據(jù)規(guī)模不大,很少有超過數(shù)十萬條記錄普筹。
鑒于此败明,MyCAT 定義了一種特殊的表,稱之為“全局表”太防,全局表具有以下特性:
? 全局表的插入妻顶、更新操作會實時在所有節(jié)點上執(zhí)行,保持各個分片的數(shù)據(jù)一致性
? 全局表的查詢操作蜒车,只從一個節(jié)點獲取
? 全局表可以跟任何一個表進行 JOIN 操作
將字典表或者符合字典表特性的一些表定義為全局表讳嘱,則從另外一個方面,很好的解決了數(shù)據(jù) JOIN 的難題酿愧。
通過全局表+基于 E-R 關(guān)系的分片策略沥潭,MyCAT 可以滿足 80%以上的企業(yè)應(yīng)用開發(fā)。
配置
全局表配置比較簡單嬉挡,不用寫 Rule 規(guī)則钝鸽,如下配置即可:
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
需要注意的是,全局表每個分片節(jié)點上都要有運行創(chuàng)建表的 DDL 語句
3.ER Join
MyCAT 借鑒了 NewSQL 領(lǐng)域的新秀 Foundation DB 的設(shè)計思路庞钢,F(xiàn)oundation DB 創(chuàng)新性的提出了 TableGroup 的概念拔恰,其將子表的存儲位置依賴于主表,并且物理上緊鄰存放基括,因此徹底解決了 JION 的效率和性能問題颜懊,根據(jù)這一思路,提出了基于 E-R 關(guān)系的數(shù)據(jù)分片策略风皿,子表的記錄與所關(guān)聯(lián)的父表記錄存放在同一個數(shù)據(jù)分片上河爹。
customer 采用 sharding-by-intfile 這個分片策略,分片在 dn1,dn2 上桐款,orders 依賴父表進行分片咸这,兩個表的關(guān)聯(lián)關(guān)系為 orders.customer_id=customer.id。于是數(shù)據(jù)分片和存儲的示意圖如下:
這樣一來鲁僚,分片 Dn1 上的的 customer 與 Dn1 上的 orders 就可以進行局部的 JOIN 聯(lián)合炊苫,Dn2 上也如此,再合并兩個節(jié)點的數(shù)據(jù)即可完成整體的 JOIN冰沙,試想一下侨艾,每個分片上 orders 表有 100 萬條,則 10 個分片就有 1 個億拓挥,基于 E-R 映射的數(shù)據(jù)分片模式唠梨,基本上解決了 80%以上的企業(yè)應(yīng)用所面臨的問題。
配置
以上述例子為例侥啤,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 的方式實現(xiàn)当叭。
目前支持 2 個表的 join,原理就是解析 SQL 語句茬故,拆分成單表的 SQL 語句執(zhí)行,然后把各個節(jié)點的數(shù)據(jù)匯集蚁鳖。
配置
支持任意配置的 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" />
重新插入數(shù)據(jù)
下面可以看下普通的 join 和 sharejoin 的區(qū)別:
5.catlet(人工智能)
解決跨分片的 SQL JOIN 的問題磺芭,遠比想象的復(fù)雜,而且往往無法實現(xiàn)高效的處理醉箕,既然如此钾腺,就依靠人工的智力,去編程解決業(yè)務(wù)系統(tǒng)中特定幾個必須跨分片的 SQL 的 JOIN 邏輯讥裤,MyCAT 提供特定的 API 供程序員調(diào)用放棒,這就是 MyCAT 創(chuàng)新性的思路——人工智能。
以一個跨節(jié)點的 SQL 為例
Select a.id,a.name,b.title from a,b where a.id=b.id
其中 a 在分片 1己英,2间螟,3 上,b 在 4损肛,5厢破,6 上,需要把數(shù)據(jù)全部拉到本地(MyCAT 服務(wù)器),執(zhí)行 JOIN 邏輯荧关,具體過程如下(只是一種可能的執(zhí)行邏輯):
EngineCtx ctx=new EngineCtx();//包含 MyCat.SQLEngine
String sql=,“ select a.id ,a.name from a ” ;
//在 a 表所在的所有分片上順序執(zhí)行下面的本地 SQL
ctx.executeNativeSQLSequnceJob(allAnodes,new DirectDBJoinHandler());
DirectDBJoinHandler 類是一個回調(diào)類溉奕,負責(zé)處理 SQL 執(zhí)行過程中返回的數(shù)據(jù)包褂傀,這里的這個類忍啤,主要目的是用 a 表返回的 ID 信息,去 b 表上查詢對于的記錄仙辟,做實時的關(guān)聯(lián):
DirectDBJoinHandler{
Private HashMap rows;//Key 為 id,value 為一行記錄的 Column 原始 Byte 數(shù)組同波,這里是a.id,a.name,b.title 這三個要輸出的字段
Public Boolean onHeader(byte[] header){
//保存 Header 信息,用于從 Row 中獲取 Field 字段值
}
Public Boolean onRowData(byte[] rowData){
String id=getColumnAsString(“ id” );
//放入結(jié)果集,b.title 字段未知叠国,所以先空著
rows.put(getColumnRawBytes(“ id” ),rowData);
//滿 1000 條未檩,發(fā)送一個查詢請求
String sql=” select b.id, b.name from b where id in (………….)” ;
//此 SQL 在 B 的所有節(jié)點上并發(fā)執(zhí)行,返回的結(jié)果直接輸出到客戶端
ctx.executeNativeSQLParallJob(allBNodes,sql ,new MyRowOutPutDataHandler(rows));
}
Public Boolean onRowFinished(){}
Public void onJobFinished(){
If(ctx.allJobFinished()){
{///used total time ….}
}
}
最后粟焊,增加一個 Job 事件監(jiān)聽器冤狡,這里是所有 Job 完成后,往客戶端發(fā)送 RowEnd 包项棠,結(jié)束整個流程悲雳。
ctx.setJobEventListener(new JobEventHandler(){public void onJobFinished(){ client.writeRowEndPackage()}});
以上提供一個 SQL 執(zhí)行框架,完全是異步的模式執(zhí)行香追,并且以后會提供更多高質(zhì)量的 API合瓢,簡化分布式數(shù)據(jù)處理,比如內(nèi)存結(jié)合文件的數(shù)據(jù) JOIN 算法透典,分組算法晴楔,排序算法等等顿苇,期待更多的牛人一起來完善
6.Spark/Storm 對 join 擴展
看到這個標(biāo)題,可能會感到很奇怪税弃,Spark 和 Storm 和 Join 有關(guān)系嗎? 有必要用 Spark纪岁,storm 嗎?
mycat 后續(xù)的功能會引入 spark 和 storm 來做跨分片的 join,大致流程是這樣的在 mycat 調(diào)用 spark,storm的 api,把數(shù)據(jù)傳送到 spark,storm则果,在 spark,storm 進行 join,在把數(shù)據(jù)傳回 mycat,mycat 在返回給客戶端蜂科。
本文摘抄于:mycat用戶指南