1 前言
Mycat目前版本支持跨分片的join,主要實現的方式有四種。
全局表
ER分片
HBT(參考MyCAT人工智能解決跨分片SQL.docx)
ShareJoin
ShareJoin在開發版中支持,前面三種方式1.3.0.1支持
2 ShareJoin
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" />
2.1相關類圖
JoinParser: SQL語句的解析
TableFilter:存解析后的各個子表
ShareJoin:執行拆分的語句管理控制,和字段,記錄的管理
ShareDBJoinHandler:第一個表執行后獲取數據的handler
ShareRowOutPutDataHandler:最后一個表執行后獲取數據的handler
EnginerCtx:執行引擎
SQLJob:SQL語句執行任務
SQLJobHandler:SQL語句執行后獲取數據的handler
BatchSQLJob:批量執行任務控制
AllJobFinishedListener:所有任務完成偵聽器
RouteService-----》HintCatletHandler---》ShareJoin
2.1測試
默認mycat的環境測試:
/*!mycat:catlet=demo.catlets.ShareJoin */ select a.*,b.id, b.name as tit from customer a,company b on a.company_id=b.id;

/*!mycat:catlet=demo.catlets.ShareJoin */ select a.*,b.id, b.name as name from orders a join customer b where a.customer_id=b.id;

/*!mycat:catlet=demo.catlets.ShareJoin */ select a.*,b.* from orders a join customer b where a.customer_id=b.id;

/*!mycat:catlet=demo.catlets.ShareJoin */ select a.id,a.user_id,a.traveldate,a.fee,a.days,b.id as nnid, b.title as tit from travelrecord a join hotnews b on b.id=a.days order by a.id ;
2.3升級
未來支持多表的跨分片Join
小表放人緩存或廣播方式
3全局表
一個真實的業務系統中,往往存在大量的類似字典表的表格,它們與業務表之間可能有關系,這種關系,可以理解為“標簽”,而不應理解為通常的“主從關系”,這些表基本上很少變動,可以根據主鍵ID進行緩存,下面這張圖說明了一個典型的“標簽關系”圖:
在分片的情況下,當業務表因為規模而進行分片以后,業務表與這些附屬的字典表之間的關聯,就成了比較棘手的問題,考慮到字典表具有以下幾個特性:
- 變動不頻繁
- 數據量總體變化不大
- 數據規模不大,很少有超過數十萬條記錄。
鑒於此,MyCAT定義了一種特殊的表,稱之為“全局表”,全局表具有以下特性:
- 全局表的插入、更新操作會實時在所有節點上執行,保持各個分片的數據一致性
- 全局表的查詢操作,只從一個節點獲取
- 全局表可以跟任何一個表進行JOIN操作
將字典表或者符合字典表特性的一些表定義為全局表,則從另外一個方面,很好的解決了數據JOIN的難題。通過全局表+基於E-R關系的分片策略,MyCAT可以滿足80%以上的企業應用開發。
3.1配置
全局表配置比較簡單,不用寫Rule規則,如下配置即可:
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
需要注意的是,全局表每個分片節點上都要有運行創建表的DDL語句。
4ER分片
MyCAT借鑒了NewSQL領域的新秀Foundation DB的設計思路,Foundation DB創新性的提出了Table Group的概念,其將子表的存儲位置依賴於主表,並且物理上緊鄰存放,因此徹底解決了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%以上的企業應用所面臨的問題。
4.1 配置
以上述例子為例,schema.xml中定義如下的分片配置:
<table name="customer" dataNode="dn1,dn2" rule="sharding-by-intfile">
<childTable name="orders" joinKey="customer_id" parentKey="id"/>
</table>
5HBT分片
解決跨分片的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<byte[],byte[]> 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算法,分組算法,排序算法等等,
期待更多的牛人一起來完善。
