一、Mycat是什么
Mycat是一個開源的分布式數據庫系統,是一個實現了 MySQL 協議的的 Server,前端用戶可以把它看作是一個數據庫代理,用 MySQL 客戶端工具和命令行訪問,而其后端可以用MySQL 原生(Native)協議與多個 MySQL 服務器通信,也可以用 JDBC 協議與大多數主流數據庫服務器通信,其核心功能是分表分庫,即將一個大表水平分割為 N 個小表,存儲在后端 MySQL 服務器里或者其他數據庫里。
Mycat 是一個近似等於 MySQL 的數據庫服務器,但它本身並不存儲數據,數據是在后端的 MySQL 上存儲的,數據可靠性以及事務等都是 MySQL 保證的。你可以用連接 MySQL 的方式去連接 Mycat(除了端口不同,默認的 Mycat 端口是 8066 而非 MySQL 的 3306,因此需要在連接字符串上增加端口信息),大多數情況下,可以用你熟悉的對象映射框架使用 Mycat,但建議對於分片表,盡量使用基礎的 SQL 語句,因為這樣能達到最佳性能,特別是幾千萬甚至幾百億條記錄的情況下。
二、Mycat的原理
Mycat 的原理並不復雜,復雜的是代碼。Mycat 的原理中最重要的一個動詞是“攔截”,它攔截了用戶發送過來的 SQL 語句,首先對 SQL 語句做了一些特定的分析:如分片分析、路由分析、讀寫分離分析、緩存分析等,然后將此 SQL 發往后端的真實數據庫,並將返回的結果做適當的處理,最終再返回給用戶。
當 Mycat 收到一個 SQL 時,會先解析這個 SQL,查找涉及到的表,然后看此表的定義,如果有分片規則,則獲取到 SQL 里分片字段的值,並匹配分片函數,得到該 SQL 對應的分片列表,然后將 SQL 發往這些分片去執行,最后收集和處理所有分片返回的結果數據,並輸出到客戶端。
三、Mycat應用場景
- 單純的讀寫分離,此時配置最為簡單,支持讀寫分離,主從切換;
- 分表分庫,對於超過 1000 萬的表進行分片,最大支持 1000 億的單表分片;
- 多租戶應用,每個應用一個庫,但應用程序只連接 Mycat,從而不改造程序本身,實現多租戶化;
- 報表系統,借助於 Mycat 的分表能力,處理大規模報表的統計;
- 替代 Hbase,分析大數據;
- 作為海量數據實時查詢的一種簡單有效方案,比如 100 億條頻繁查詢的記錄需要在 3 秒內查詢出來結果,除了基於主鍵的查詢,還可能存在范圍查詢或其他屬性查詢,此時 Mycat 可能是最簡單有效的選擇。
四、Mycat不適用場景
1. 非分片字段查詢
Mycat中的路由結果是通過分片字段和分片方法來確定的。
如果查詢條件中有分片字段,查詢會通過計算路由落到某個具體的分片,並將請求路由到指定的數據庫上執行。
如果查詢條件中沒有分片字段條件,此時Mycat無法計算路由,便發送到所有節點上執行。如果DB節點很多,這會極大消耗Mycat和MySQL數據庫資源。
2. 分頁排序
先看一下Mycat是如何處理分頁操作的,假如有如下Mycat分庫方案:
一張表有30份數據分布在3個分片DB上,具體數據分布如下:
DB1:[0,1,2,3,4,10,11,12,13,14] DB2:[5,6,7,8,9,16,17,18,19] DB3:[20,21,22,23,24,25,26,27,28,29]
這個示例的場景中沒有查詢條件,所以都是全分片查詢,也就沒有假定該表的分片字段和分片方法。
當應用執行如下分頁查詢時:
select * fromtable limit 2;
Mycat將該SQL請求分發到各個DB節點去執行,並接收各個DB節點的返回結果:
DB1: [0,1] DB2: [5,6] DB3: [20,21]
但Mycat向應用返回的結果集取決於哪個DB節點最先返回結果給Mycat。如果Mycat最先收到DB1節點的結果集,那么Mycat返回給應用端的結果集為 [0,1],如果Mycat最先收到DB2節點的結果集,那么返回給應用端的結果集為 [5,6]。也就是說,相同情況下,同一個SQL,在Mycat上執行時會有不同的返回結果。
在Mycat中執行分頁操作時必須顯示加上排序條件才能保證結果的正確性,下面看一下Mycat對排序分頁的處理邏輯。
假如在前面的分頁查詢中加上了排序條件(假如表數據的列名為id):
select * fromtable orderby id limit 2;
在有排序呢條件的情況下,Mycat接收到各個DB節點的返回結果后,對其進行最小堆運算,計算出所有結果集中最小的兩條記錄 [0,1] 返回給應用。
但是,當排序分頁中有 偏移量 (offset)時,處理邏輯又有不同。假如應用的查詢SQL如下:
select * fromtable order by id limit 5,2;
如果按照上述排序分頁邏輯來處理,那么處理結果如下圖:
Mycat將各個DB節點返回的數據 [10,11],[16,17], [20,21] 經過最小堆計算后返回給應用的結果集是 [10,11]。可是,對於應用而言,該表的所有數據明明是 0-29 這30個數據的集合,limit 5,2 操作返回的結果集應該是 [5,6],如果返回 [10,11] 則是錯誤的處理邏輯。
所以Mycat在處理 有偏移量的排序分頁 時是另外一套邏輯——改寫SQL 。
Mycat在下發有 limit m,n 的SQL語句時會對其進行改寫,改寫成 limit 0, m+n 來保證查詢結果的邏輯正確性。所以,Mycat發送到后端DB上的SQL語句是:
select * fromtable order by id limit 0,7;
各個DB返回給Mycat的結果集是:
DB1: [0,1,2,3,4,10,11] DB2: [5,6,7,8,9,16,17] DB3: [20,21,22,23,24,25,26]
經過最小堆計算后得到最小序列 [0,1,2,3,4,5,6] ,然后返回偏移量為5的兩個結果為 [5,6] 。
雖然Mycat返回了正確的結果,但是仔細推敲發現這類操作的處理邏輯是及其消耗(浪費)資源的。應用需要的結果集為2條,Mycat中需要處理的結果數為21條。也就是說,對於有 t 個DB節點的全分片 limit m, n 操作,Mycat需要處理的數據量為 (m+n)*t 個。比如實際應用中有50個DB節點,要執行limit 1000,10操作,則Mycat處理的數據量為 50500 條,返回結果集為10,當偏移量更大時,內存和CPU資源的消耗則是數十倍增加。
3. 任意表的join
先看一下在單庫中JOIN中的場景。假設在某單庫中有 player 和 team 兩張表,player 表中的 team_id 字段與 team 表中的id 字段相關聯。
JOIN操作的SQL如下:
selectp_name,t_name from player p, team t where p.no = 3 and p.team_id = t.id;
此時能查詢出結果。
如果將這兩個表的數據分庫后,相關聯的數據可能分布在不同的DB節點上:
這個SQL在各個單獨的分片DB中都查不出結果,也就是說Mycat不能查詢出正確的結果集。
4. 分布式事務
Mycat並沒有根據二階段提交協議實現 XA事務,而是只保證 prepare 階段數據一致性的 弱XA事務 ,實現過程如下:
應用開啟事務后Mycat標識該連接為非自動提交,比如前端執行:
mysql>begin;
Mycat不會立即把命令發送到DB節點上,等后續下發SQL時,Mycat從連接池獲取非自動提交的連接去執行。
Mycat會等待各個節點的返回結果,如果都執行成功,Mycat給該連接標識為 Prepare Ready 狀態,如果有一個節點執行失敗,則標識為 Rollback 狀態。
執行完成后Mycat等待前端發送 commit 或 rollback 命令。發送 commit 命令時,Mycat檢測當前連接是否為 Prepare Ready 狀態,若是,則將 commit 命令發送到各個DB節點。
但是,這一階段是無法保證一致性的,如果一個DB節點在 commit 時故障,而其他DB節點 commit 成功,Mycat會一直等待故障DB節點返回結果。Mycat只有收到所有DB節點的成功執行結果才會向前端返回 執行成功 的包,此時Mycat只能一直 waiting 直至TIMEOUT,導致事務一致性被破壞。
本系列是基於1.6-RELEASE (2.0尚在開發)
參考:https://blog.csdn.net/gaobudong1234/article/details/79581846