一次可貴的分庫分表實踐

背景

前不久發過兩篇關於分表的文章:算法

從標題能夠看得出來,當時咱們只作了分表;仍是因爲業務發展,截止到如今也作了分庫,目前看來都還比較順利,因此藉着腦子還記得清楚來一次覆盤。數據庫

先來回顧下整個分庫分表的流程以下:多線程

整個過程也很好理解,基本符合大部分公司的一個發展方向。併發

不多會有業務一開始就會設計爲分庫分表,雖然說這樣會減小後續的坑,但部分公司剛開始都是以業務爲主。性能

直到業務發展到單表沒法支撐時,天然而然會考慮分表甚至分庫的事情。大數據

因而本篇會做一次總結,以前提過的內容可能會再重複一次。線程

分表

首先討論下什麼樣的狀況下適合分表?設計

根據個人經驗來看,當某張表的數據量已經達到千萬甚至上億,同時日增數據量在 2% 以上。3d

固然這些數字並非絕對的,最重要的仍是對這張表的寫入和查詢都已經影響到正常業務執行,好比查詢速度明顯降低,數據庫總體 IO 居高不下等。code

而談到分表時咱們着重討論的仍是水平分表;

也就是將一張大表數據經過某種路由算法將數據儘量的均勻分配到 N 張小表中。

Range

而分表策略也有好幾種,分別適用不一樣的場景。

首先第一種是按照範圍劃分,好比咱們能夠將某張表的建立時間按照日期劃分存爲月表;也能夠將某張表的主鍵按照範圍劃分,好比 【1~10000】在一張表,【10001~20000】在一張表,以此類推。

這樣的分表適合須要對數據作歸檔處理,好比系統默認只提供近三個月曆史數據的查詢功能,這樣也方便操做;只須要把三月以前的數據單獨移走備份保存便可)。

這個方案有好處也有弊端:

  • 好處是自帶水平擴展,不須要過多幹預。
  • 缺點是可能會出現數據不均勻的狀況(好比某個月請求暴增)。

Hash

按照日期這樣的範圍分表當然簡單,但適用範圍仍是比較窄;畢竟咱們大部分的數據查詢都不想帶上時間。

好比某個用戶想查詢他產生的全部訂單信息,這是很常見的需求。

因而咱們分表的維度就得改改,分表算法能夠採用主流的 hash+mod 的組合。

這是一個經典的算法,大名鼎鼎的 HashMap 也是這樣來存儲數據。

假設咱們這裏將原有的一張大表訂單信息分爲 64 張分表:

這裏的 hash 即是將咱們須要分表的字段進行一次散列運算,使得通過散列的數據儘量的均勻而且不重複。

固然若是自己這個字段就是一個整形而且不重複也能夠省略這個步驟,直接進行 Mod 獲得分表下標便可。

分表數量選擇

至於這裏的分表數量(64)也是有講究的,具體設爲多少這個沒有標準值,須要根據自身業務發展,數據增量進行預估。

根據我我的的經驗來看,至少須要保證分好以後的小表在業務發展的幾年以內都不會出現單表數據量過大(好比達到千萬級)。

我更傾向於在數據庫可接受的範圍內儘量的增大這個分表數,畢竟若是後續小表也達到瓶頸須要再進行一次分表擴容,那是很是痛苦的。

目前筆者還沒經歷這一步,因此本文沒有相關介紹。

可是這個數量又不是瞎選的,和 HashMap 同樣,也建議得是 2^n,這樣能夠方便在擴容的時儘量的少遷移數據。

Range + Hash

固然還有一種思路,RangeHash 是否能夠混用。

好比咱們一開始採用的是 Hash 分表,可是數據增加巨大,致使每張分表數據很快達到瓶頸,這樣就不得再也不作擴容,好比由 64 張表擴容到 256 張。

但擴容時想要作到不停機遷移數據很是困難,即使是停機,那停多久呢?也很差說。

因此咱們是否能夠在 Mod 分表的基礎上再分爲月表,藉助於 Range 自身的擴展性就不用考慮後續數據遷移的事情了。

這種方式理論可行,但我沒有實際用過,給你們的思路作個參考吧。

煩人的數據遷移

分表規則弄好後其實只是完成了分表的第一步,真正麻煩的是數據遷移,或者說是如何作到對業務影響最小的數據遷移。

除非是一開始就作了分表,因此數據遷移這一步驟確定是跑不掉的。

下面整理下目前咱們的作法供你們參考:

  1. 一旦分表上線後全部的數據寫入、查詢都是針對於分表的,因此原有大表內的數據必須得遷移到分表裏,否則對業務的影響極大。
  2. 咱們估算了對一張 2 億左右的表進行遷移,本身寫的遷移程序,大概須要花 4~5 天的時間才能完成遷移。
  3. 意味着這段時間內,之前的數據對用戶是不可見的,顯然這樣業務不能接受。
  4. 因而咱們作了一個兼容處理:分表改造上線後,全部新產生的數據寫入分表,但對歷史數據的操做還走老表,這樣就少了數據遷移這一步驟。
  5. 只是須要在操做數據以前作一次路由判斷,當新數據產生的足夠多時(咱們是兩個月時間),幾乎全部的操做都是針對於分表,再從庫啓動數據遷移,數據遷移完畢後將原有的路由判斷去掉。
  6. 最後全部的數據都從分表產生和寫入。

至此整個分表操做完成。

業務兼容

同時分表以後還須要兼容其餘業務;好比原有的報表業務、分頁查詢等,如今來看看咱們是如何處理的。

報表

首先是報表,沒分表以前之間查詢一張表就搞定了,如今不一樣,由一張表變爲 N 張表。

因此原有的查詢要改成遍歷全部的分表,考慮到性能能夠利用多線程併發查詢分表數據而後彙總。

不過只依靠 Java 來對這麼大量的數據作統計分析仍是不現實,剛開始能夠應付過去,後續還得用上大數據平臺來處理。

查詢

再一個是查詢,原有的分頁查詢確定是不能用了,畢竟對上億的數據分頁其實沒什麼意義。

只能提供經過分表字段的查詢,好比是按照訂單 ID 分表,那查詢條件就得帶上這個字段,否則就會涉及到遍歷全部表。

這也是全部分表以後都會遇到的一個問題,除非不用 MySQL 這類關係型數據庫。

分庫

分表完成後能夠解決單表的壓力,但數據庫自己的壓力卻沒有降低。

咱們在完成分表以後的一個月內又因爲數據庫裏「其餘表」的寫入致使整個數據庫 IO 增長,並且這些「其餘表」還和業務關係不大。

也就是說一些無關緊要的數據致使了總體業務受影響,這是很是不划算的事情。

因而咱們便把這幾張表單獨移到一個新的數據庫中,徹底和現有的業務隔離開來。

這樣就會涉及到幾個改造:

  1. 應用自身對這些數據的查詢、寫入都要改成調用一個獨立的 Dubbo 服務,由這個服務對遷移的表進行操做。
  2. 暫時不作數據遷移,因此查詢時也得按照分表那樣作一個兼容,若是查詢老數據就要在當前庫查詢,新數據就要調用 Dubbo 接口進行查詢。
  3. 對這些表的一些關聯查詢也得改造爲查詢 Dubbo 接口,在內存中進行拼接便可。
  4. 若是數據量確實很大,也可將同步的 Dubbo 接口換爲寫入消息隊列來提升吞吐量。

目前咱們將這類數據量巨大但對業務不太影響的表單獨遷到一個庫後,數據庫的總體 IO 降低明顯,業務也恢復正常。

總結

最後咱們還須要作一步歷史數據歸檔的操做,將 N 個月以前的數據要按期遷移到 HBASE 之類存儲,保證 MySQL 中的數據一直保持在一個可接受的範圍。

而歸檔數據的查詢便依賴於大數據提供服務。

本次分庫分表是一次很是可貴的實踐操做,網上大部分的資料都是在汽車出廠前就換好了輪胎。

而咱們大部分碰到的場景都是要對高速路上跑着的車子換胎,一不當心就「車毀人亡」!

有更好的方式方法歡迎你們評論區留言討論。

你的點贊與分享是對我最大的支持

相關文章
相關標籤/搜索