聽雲的分庫分表實踐【轉】

前段時間,Oracle官方發佈了MySQL 5.7的GA版本。新版本中實現了真正意義的並行複製(基於Group Commit的Group Replication),而再也不是基於schema的並行複製。這一特性極大的改善了特定場景下的主從複製延遲太高的情況。隨着MySQL成熟度的提高,愈來愈多的用戶選擇使用MySQL存放自家的數據,其中不乏使用MySQL來存放大量數據的。前端

在過去的半年多時間裏,聽雲業務量呈爆發式增加,後端的數據量由去年第一季度的幾TB增加到幾十TB,業務量翻了十幾倍。後端應用及數據庫面臨的一個突出的問題就是頻繁的進行擴容來應對前端流量的增加。數據庫層面咱們使用MySQL來分佈式存儲業務數據,數據庫集羣的架構也比較簡單,咱們使用開源中間件Amoeba來實現數據的拆分和讀寫分離。Amoeba後端有幾百個數據庫的節點組,每一個節點組中都包含一對主從實例。master實例負責接受write請求,slave負責接受query請求。以下圖:mysql

2.23.png 

正確的拆分姿式sql

隨着可選擇的開源中間件愈來愈多,好多數據量並非很大的使用者都會過早的考慮水平拆分數據庫。但其實過早的水平拆分未見得是一件有意義的事情。主要緣由有兩個:一個方面是水平拆分會對現網的業務形成衝擊,若是系統在設計之初就沒有考慮事後續要進行拆分的話,這個衝擊就會被放大。好比業務中有大量的多表join的查詢,或者是對事務有強一致性的要求時,水平拆分就捉襟見肘了。另外一方面,若是過早的進行了水平拆分,那麼到達必定程度後再想要垂直進行拆分時,代價是很大的。以聽雲app爲例,當咱們業務庫拆成8個分片後,有一天發現數據增加的很快,因而決定對其進行垂直拆分,將小時緯度和天緯度的數據拆分到一個新的實例上去,這時咱們不得不一樣時部署8個節點組來將現有的8個分片上的小時緯度和天緯度的數據遷移出來。工做量至關大。若是水平拆分到了64個片,那麼這時要想再作垂直拆分,保證累的你不要不要的。數據庫

因此更合理的路線是這樣的,首先對業務數據進行垂直拆分,本來一個庫按業務單元垂直拆分紅多個庫,同時應用中配置多個數據源或者使用中間件來訪問拆分後的多個庫,對應用自己來講,基本沒作什麼改動,可是後端存儲的容量和性能卻翻了好幾倍。若是某天出現瓶頸以後,再來考慮水平拆分的事情。後端

優雅的從n到2n架構

2.45.png 

水平擴展過程當中最讓人頭疼的是數據的遷移,以上圖中遷移mod(mobile_app_id,4)=2的數據爲例,最開始的作法是先建立兩個新的節點組shared0_new和shared2,拿shared0的全備恢復到shared0_new和shared2,而後在shared0_new上刪除mod(mobile_app_id,4)=2的數據,在shared2上刪除mod(mobile_app_id,4)=0的數據,刪除操做完成後shared0_new、shared2與shared0作同步,同步刪除操做執行過程當中的數據增量。同步追上以後,切換amoeba的路由規則,而後下線shared0。這種方式問題不少,首先時耗很高很高,delete完了以後並不能釋放存儲空間,還要optimize table,一樣也是一個漫長的過程。針對大表的delete會產生一個很大的transaction,會在系統表空間中申請很大一塊undo,delete完成後事務提交。這個undo空間並不會釋放,而是直接給其餘事務複用,這無疑會浪費不少存儲空間。app

後來咱們想到一個便捷的辦法,就是利用mysqldump的—where參數,在備份數據的時候加一個mod(mobile_app_id,4)=2的參數,就能夠單獨備份出餘數爲2的數據,而後拿這個邏輯備份恢復到shared2上去,高效且優雅。運維

數據傾斜分佈式

MySQL分佈式存儲不可避免的一個問題就是數據傾斜。業務在運行一段時間以後,會發現少部分shared數據增量特別快,緣由是該shared上面部分用戶的數據量較大。對於數據傾斜問題咱們目前的措施是將這些shared遷移到1TB存儲上來,但這並不是長久之計。所以咱們目前正在作一些新的嘗試,好比對Amoeba作了一下擴展,擴展後的Amoeba支持將某一個mobile_app_id的數據單獨指向後端一個shared節點組,即一個shared只存放一個用戶的數據,同時採用ToKuDB存儲引擎來存儲這部分數據,ToKuDB可以對數據進行有效的壓縮,除了查詢性能稍有損耗以外,基本具有InnoDB引擎所擁有的特色,並且在線表結構變動比InnoDB快好幾倍不止。這些測試基本已經進入尾聲,很快將會應用到生產環境。工具

分佈式join

分佈式join在業界仍沒有完美的解決方案,好在聽雲業務在設計之初就從業務上避免了多表的join,在業務庫中,報表中的每一個緯度都會有一張表與之對應,所以查詢某個緯度直接就會查詢後端的某張表,都是在每張表上作一些操做。目前比較流行的分佈式join的解決方案主要有兩種:

一、全局表的形式。舉個例子,A表 join B表,B表分佈式存儲在多個shared上,若是A表比較小,能夠在全部的shared上都存一份A表的全量數據。那麼就能夠很高效的作join。看起來很美好,可是限制不少,應用的場景也頗有限。

二、E-R形式。舉個栗子,用戶表user(id,name)和訂單表order(id,uid,detail),按用戶id分片,order表的uid引用自user表的id。存放訂單時,首先肯定該訂單對應用戶所在的shared,而後將訂單記錄插入到用戶所在的shared上去,這樣檢索某個用戶全部的訂單時,就能夠避免跨庫join低效的操做。

目前的開源中間件中,MyCat對分佈式join處理的是比較細膩的。阿里的DRDS對於分佈式join的處理也是這樣的思路。

MySQL擅長什麼

任何一種工具可能都只是解決某一個領域的問題,確定不是放之四海而皆準的。正確的使用方式是讓工具作本身擅長的事情。關係型數據庫擅長的是結構化的查詢,自己並不擅長巨量數據的清洗。咱們在出2015年度APP行業均值數據報表時,須要將後端全部shared上的相關數據彙總起來而後作進一步的分析,這些數據最終彙總在5張表中,每張表都有幾億條的記錄。而後對五、6個字段group by以後取某些指標的 sum值,最初嘗試在MySQL中處理這些數據,MySQL實例給出24GB的內存,結果OOM了好幾回也沒有出結果。最後把數據拉到了hadoop集羣上,使用impala引擎來彙總數據,處理最大的表近7億條記錄,9min左右出結果。因此,不要有all  in  one的想法,要讓系統中的每一個組件作本身擅長的事情。

分佈式MySQL架構下的運維

MySQL分佈式雖然解決了存儲和性能問題,可是在運維支持過程當中卻帶來了一些痛點。

一、跨分片統計數據。中間件是沒法對後端的全量數據作查詢的,相似年度APP行業均值報表這樣的跨分片的全量數據的查詢,只能使用自動化腳本從後端逐個shared上提取數據,最終再彙總。

二、DML。常常會有變動表結構的需求,這樣的操做大部分中間件是支持不了的,若是隻有一個庫好說,當後端幾十個shared時,就比較頭疼了,目前咱們並無很好的處理辦法,只能使用自動化腳本批量到後端shared上執行命令,執行完成後,運行一個校驗的腳本,人工覈對校驗腳本的輸出內容。

應對這樣的情景,髮型必然會稍顯凌亂,可是目前仍舊很無奈,有必要從新設計一下咱們的腳本,寫一個輸出更加友好,徹底自動化的工具出來。

相關文章
相關標籤/搜索