摘要:煩人的數據遷移分表規則弄好后其實只是完成了分表的第一步,真正麻煩的是數據遷移,或者說是如何做到對業務影響最小的數據遷移。
背景
前不久發過兩篇關于分表的文章:
一次分表踩坑實踐的探討
分表后需要注意的二三事
從標題可以看得出來,當時我們只做了分表;還是由于業務發展,截止到現在也做了分庫,目前看來都還比較順利,所以借著腦子還記得清楚來一次復盤。
先來回顧下整個分庫分表的流程如下:
整個過程也很好理解,基本符合大部分公司的一個發展方向。
很少會有業務一開始就會設計為分庫分表,雖說這樣會減少后續的坑,但部分公司剛開始都是以業務為主。
直到業務發展到單表無法支撐時,自然而然會考慮分表甚至分庫的事情。
于是本篇會作一次總結,之前提過的內容可能會再重復一次。
分表首先討論下什么樣的情況下適合分表?
根據我的經驗來看,當某張表的數據量已經達到千萬甚至上億,同時日增數據量在 2% 以上。
當然這些數字并不是絕對的,最重要的還是對這張表的寫入和查詢都已經影響到正常業務執行,比如查詢速度明顯下降,數據庫整體 IO 居高不下等。
而談到分表時我們著重討論的還是水平分表;
也就是將一張大表數據通過某種路由算法將數據盡可能的均勻分配到 N 張小表中。
Range而分表策略也有好幾種,分別適用不同的場景。
首先第一種是按照范圍劃分,比如我們可以將某張表的創建時間按照日期劃分存為月表;也可以將某張表的主鍵按照范圍劃分,比如 【1~10000】在一張表,【10001~20000】在一張表,以此類推。
這樣的分表適合需要對數據做歸檔處理,比如系統默認只提供近三個月歷史數據的查詢功能,這樣也方便操作;只需要把三月之前的數據多帶帶移走備份保存即可)。
這個方案有好處也有弊端:
好處是自帶水平擴展,不需要過多干預。
缺點是可能會出現數據不均勻的情況(比如某個月請求暴增)。
Hash按照日期這樣的范圍分表固然簡單,但適用范圍還是比較窄;畢竟我們大部分的數據查詢都不想帶上時間。
比如某個用戶想查詢他產生的所有訂單信息,這是很常見的需求。
于是我們分表的維度就得改改,分表算法可以采用主流的 hash+mod 的組合。
這是一個經典的算法,大名鼎鼎的 HashMap 也是這樣來存儲數據。
假設我們這里將原有的一張大表訂單信息分為 64 張分表:
這里的 hash 便是將我們需要分表的字段進行一次散列運算,使得經過散列的數據盡可能的均勻并且不重復。
當然如果本身這個字段就是一個整形并且不重復也可以省略這個步驟,直接進行 Mod 得到分表下標即可。
分表數量選擇至于這里的分表數量(64)也是有講究的,具體設為多少這個沒有標準值,需要根據自身業務發展,數據增量進行預估。
根據我個人的經驗來看,至少需要保證分好之后的小表在業務發展的幾年之內都不會出現單表數據量過大(比如達到千萬級)。
我更傾向于在數據庫可接受的范圍內盡可能的增大這個分表數,畢竟如果后續小表也達到瓶頸需要再進行一次分表擴容,那是非常痛苦的。
目前筆者還沒經歷這一步,所以本文沒有相關介紹。
但是這個數量又不是瞎選的,和 HashMap 一樣,也建議得是 2^n,這樣可以方便在擴容的時盡可能的少遷移數據。
Range + Hash當然還有一種思路,Range 和 Hash 是否可以混用。
比如我們一開始采用的是 Hash 分表,但是數據增長巨大,導致每張分表數據很快達到瓶頸,這樣就不得不再做擴容,比如由 64 張表擴容到 256 張。
但擴容時想要做到不停機遷移數據非常困難,即便是停機,那停多久呢?也不好說。
所以我們是否可以在 Mod 分表的基礎上再分為月表,借助于 Range 自身的擴展性就不用考慮后續數據遷移的事情了。
這種方式理論可行,但我沒有實際用過,給大家的思路做個參考吧。
煩人的數據遷移分表規則弄好后其實只是完成了分表的第一步,真正麻煩的是數據遷移,或者說是如何做到對業務影響最小的數據遷移。
除非是一開始就做了分表,所以數據遷移這一步驟肯定是跑不掉的。
下面整理下目前我們的做法供大家參考:
一旦分表上線后所有的數據寫入、查詢都是針對于分表的,所以原有大表內的數據必須得遷移到分表里,不然對業務的影響極大。
我們估算了對一張 2 億左右的表進行遷移,自己寫的遷移程序,大概需要花 4~5 天的時間才能完成遷移。
意味著這段時間內,以前的數據對用戶是不可見的,顯然這樣業務不能接受。
于是我們做了一個兼容處理:分表改造上線后,所有新產生的數據寫入分表,但對歷史數據的操作還走老表,這樣就少了數據遷移這一步驟。
只是需要在操作數據之前做一次路由判斷,當新數據產生的足夠多時(我們是兩個月時間),幾乎所有的操作都是針對于分表,再從庫啟動數據遷移,數據遷移完畢后將原有的路由判斷去掉。
最后所有的數據都從分表產生和寫入。
至此整個分表操作完成。
業務兼容同時分表之后還需要兼容其他業務;比如原有的報表業務、分頁查詢等,現在來看看我們是如何處理的。
報表首先是報表,沒分表之前之間查詢一張表就搞定了,現在不同,由一張表變為 N 張表。
所以原有的查詢要改為遍歷所有的分表,考慮到性能可以利用多線程并發查詢分表數據然后匯總。
不過只依靠 Java 來對這么大量的數據做統計分析還是不現實,剛開始可以應付過去,后續還得用上大數據平臺來處理。
查詢再一個是查詢,原有的分頁查詢肯定是不能用了,畢竟對上億的數據分頁其實沒什么意義。
只能提供通過分表字段的查詢,比如是按照訂單 ID 分表,那查詢條件就得帶上這個字段,不然就會涉及到遍歷所有表。
這也是所有分表之后都會遇到的一個問題,除非不用 MySQL 這類關系型數據庫。
分庫分表完成后可以解決單表的壓力,但數據庫本身的壓力卻沒有下降。
我們在完成分表之后的一個月內又由于數據庫里“其他表”的寫入導致整個數據庫 IO 增加,而且這些“其他表”還和業務關系不大。
也就是說一些可有可無的數據導致了整體業務受影響,這是非常不劃算的事情。
于是我們便把這幾張表多帶帶移到一個新的數據庫中,完全和現有的業務隔離開來。
這樣就會涉及到幾個改造:
應用自身對這些數據的查詢、寫入都要改為調用一個獨立的 Dubbo 服務,由這個服務對遷移的表進行操作。
暫時不做數據遷移,所以查詢時也得按照分表那樣做一個兼容,如果查詢老數據就要在當前庫查詢,新數據就要調用 Dubbo 接口進行查詢。
對這些表的一些關聯查詢也得改造為查詢 Dubbo 接口,在內存中進行拼接即可。
如果數據量確實很大,也可將同步的 Dubbo 接口換為寫入消息隊列來提高吞吐量。
目前我們將這類數據量巨大但對業務不太影響的表多帶帶遷到一個庫后,數據庫的整體 IO 下降明顯,業務也恢復正常。
總結最后我們還需要做一步歷史數據歸檔的操作,將 N 個月之前的數據要定期遷移到 HBASE 之類存儲,保證 MySQL 中的數據一直保持在一個可接受的范圍。
而歸檔數據的查詢便依賴于大數據提供服務。
本次分庫分表是一次非常難得的實踐操作,網上大部分的資料都是在汽車出廠前就換好了輪胎。
而我們大部分碰到的場景都是要對高速路上跑著的車子換胎,一不小心就“車毀人亡”。
有更好的方式方法歡迎大家評論區留言討論。
你的點贊與分享是對我最大的支持
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75915.html
摘要:文章共字,閱讀大約需要分鐘概述在如今海量數據充斥的互聯網環境下,分庫分表的意義我想在此處就不用贅述了。 showImg(https://segmentfault.com/img/remote/1460000017453449); 文章共 1796字,閱讀大約需要 4分鐘 ! 概 述 在如今海量數據充斥的互聯網環境下,分庫分表的意義我想在此處就不用贅述了。而分庫分表目前流行的方案最起碼...
閱讀 3485·2021-10-18 13:30
閱讀 2940·2021-10-09 09:44
閱讀 1963·2019-08-30 11:26
閱讀 2287·2019-08-29 13:17
閱讀 757·2019-08-29 12:17
閱讀 2246·2019-08-26 18:42
閱讀 470·2019-08-26 13:24
閱讀 2951·2019-08-26 11:39