盤點分庫分表中件間Mycat中的坑
一、介紹
公司最近在搞服務分離,數據切分的工作,因為訂單和訂單項表的數據量實在過大,而且每天都是以50萬的數據量在增長,基于現狀,項目組決定采用分庫的方式來解決當前遇到的問題。
那具體怎么切分呢?
分庫的策略其實還比較簡單,主要是要確定分片的字段和策略。
最開始是想通過主鍵ID的奇、偶數來分兩個庫,order_1庫主要用于存儲奇數的ID,order_2庫主要用于存儲偶數的ID。
但是這種切分,局限性非常大,因為最多只能分兩個庫,如果隨著數據量的增大,后面就沒很難在分了。
之后又想到了另一個分片字段:城市ID,因為訂單表上有城市ID的屬性,我們可以基于此進行分庫,但是全國有幾百個城市,不可能分幾百個庫或者表,最后的討論結果是:
- 城市ID的生成固定大小,默認三位數,100~999
- 將訂單表分成三個庫,order_1、order_2、order_3
- 當城市ID 在100~399區間,就存儲到order_1庫
- 當城市ID 在400~699區間,就存儲到order_2庫
- 當城市ID 在700~999區間,就存儲到order_3庫
通過城市ID進行分片,如果后期訂單數據量進一步過大,也可以進一步的分庫!
基于Mysql數據庫,使用最廣、最成熟的分布式中間件當屬于Mycat。
但是,自從采用Mycat中間件進行分庫之后,發現了非常多的坑,下面我們就一起來看看這些坑點!
二、細數Mycat中的坑點
2.1、分頁查詢會出現全表掃描
當我們把功能上線之后,測試人員在頁面上從末尾頁不停的往前分頁查詢訂單數據的時候,運維平臺突然報監控到很多慢 SQL 報警。
以下是運維平臺監控到的慢sql語句。
- SELECT id FROM order
- WHERE OrderCreateTime BETWEEN '2021-05-01 00:00:00' AND '2021-06-01 00:00:00'
- ORDER BY id DESC
- LIMIT 0, 151400
于是,運維同學開始找到我們,說我們程序有問題,并在群里開始吐槽我們開發寫的啥玩意,但是我們開發堅信程序沒有問題,通過查詢日志,我們排查到代碼的查詢語句是長這樣的。
- SELECT id FROM order
- WHERE OrderCreateTime BETWEEN '2021-05-01 00:00:00' AND '2021-06-01 00:00:00'
- ORDER BY id DESC
- LIMIT 151300, 100
與實際運維給的慢sql語句中的LIMIT 0, 151400完全不符合。
包括我們自己也 review 了代碼,把 sql日志也截了圖,找技術總監說理去。
之后,當測試人員再次點擊分頁查詢的時候,運維又監控到了LIMIT 0, 151400這種怪異的SQL,我們花了好幾個小時排查,在本地跑測試,還是沒發現什么問題,真的感覺到了要懷疑人生了!
當多次測試的時候,這個問題每次都能復現,讓我想起了一個問題,是不是 Mycat 分頁的時候,對全表掃描了。
后來經過查閱資料,才發現真有這個坑!
在分庫分表的情況下,宕 limit 的開始位置特別大的時候,例如大于某表的總行數時,mycat 將查詢各個分表的結果集返,然后在mycat中進行合并和排序,再返回結果。
例如,當你原始的 sql 語句是這樣的:
- SELECT * FROM table_name WHERE type='xxx' ORDER BY create_time LIMIT 10000,1000
通過 mycat 執行的結果,會是這樣的:
- SELECT * FROM table_name WHERE type='xxx' ORDER BY create_time LIMIT 0,11000
結果集特別大的情況會導致查詢很慢,嚴重的情況會直接導致 mycat OOM!
因此,在分庫分表的情況,不要用 mycat 進行大批量的數據分頁查詢,通過條件過濾,減小分頁的數據量大小!
2.2、子查詢結果偶爾不完整
當通過某些條件,篩選訂單項數據時,測試人員反饋某些數據偶爾出現不完整。
具體SQL操作如下:
- select id,productName
- from orderItem
- where orderId in (
- select id from order where userName = '張三'
- )
預期的查詢結果時:
- 1,"巧克力"
- 2,"可樂"
- 3,"果凍"
- 4,"蘋果手機"
但是實際查詢的時候,有時候的結果如下:
- 1,"巧克力"
- 2,"可樂"
- 4,"蘋果手機"
在網上查詢了相關的問題,在分庫分表的情況下,子查詢出了偶爾查詢不到完整數據外,還會出現 mycat 內部死鎖,因此盡量在代碼中不要使用子查詢,而是采用主鍵ID或是索引字段進行單表查詢,這樣效率會大大提升!
2.3、跨分片join問題
由于歷史代碼的緣故,訂單服務里面存在很多各種連表操作,例如:
- select a.*,b.accountName,c.address
- from order a
- left join account b on a.accountId = b.id
- left join account_address c on b.id = c.accountId
- where a.orderId = 11110011
但是在走 mycat 查詢之后,直接報錯!
原因是:mycat 目前只支持兩張分片表的 Join,如果要支持多張表需要自己改造程序代碼或者改造Mycat的源代碼。
2.4、部分SQL語法不支持
在實際使用的時候,發現還有部分sql語句是不支持的。
復制插入(不支持)
- insert into......select.....
復雜更新(不支持)
- update a, b set a.remark='備注' where a.id=b.id;
復雜刪除(不支持)
- delete a from a join b on a.id=b.id;
還有就是不支持跨庫連表操作!
2.5、不支持存儲過程創建和調用
有一點,需要大家注意的,在走 mycat 中間件的方式與數據庫連接的時候,如果代碼中寫了存儲過程等語句,是 mycat 是不支持調用的,因此盡量不要使用!
三、小結
雖然上面介紹了 mycat 有一些坑,但是這些坑,通過一些優化手段還是可以避免的。
實際上,mycat 作為分庫分表的中間件,也有許多的優勢,例如下面官網的介紹。
據了解,mycat 是目前最成熟、使用最廣的中間件,因此大家在使用的時候,不需要帶有啥顧慮,對于以上的坑點,盡可能的避免。