分庫分表---理論
當一張表的數據達到幾千萬時,查詢一次所花的時間會變長。業界公認MySQL單表容量在 1千萬 以下是最佳狀態,因為這時它的BTREE索引樹高在3~5之間。
數據切分可以分為:垂直切分
和水平切分
。
一、垂直切分
垂直切分又可以分為: 垂直分庫
和垂直分表
。
1、垂直分庫
概念
就是根據業務耦合性,將關聯度低的不同表存儲在不同的數據庫。做法與大系統拆分為多個小系統類似,按業務分類進行獨立划分。與"微服務治理"的做法相似,
每個微服務使用單獨的一個數據庫。
如圖:

說明
一開始我們是單體服務,所以只有一個數據庫,所有的表都在這個庫里。
后來因為業務需求,單體服務變成微服務治理。所以將之前的一個商品庫,拆分成多個數據庫。每個微服務對於一個數據庫。
2、垂直分表
概念
把一個表的多個字段分別拆成多個表,一般按字段的冷熱拆分,熱字段一個表,冷字段一個表。從而提升了數據庫性能。
如圖:

說明
一開始商品表中包含商品的所有字段,但是我們發現:
1.商品詳情和商品屬性字段較長
。2.商品列表的時候我們是不需要顯示商品詳情和商品屬性信息,只有在點進商品商品的時候才會展示商品詳情信息
。
所以可以考慮把商品詳情和商品屬性單獨切分一張表,提高查詢效率。
3、垂直切分優缺點
優點
- 解決業務系統層面的耦合,業務清晰
- 與微服務的治理類似,也能對不同業務的數據進行分級管理、維護、監控、擴展等
- 高並發場景下,垂直切分一定程度的提升IO、數據庫連接數、單機硬件資源的瓶頸
缺點
- 分庫后無法Join,只能通過接口聚合方式解決,提升了開發的復雜度
- 分庫后分布式事務處理復雜
- 依然存在單表數據量過大的問題(需要水平切分)
二、水平切分
當一個應用難以再細粒度的垂直切分或切分后數據量行數巨大,存在單庫讀寫、存儲性能瓶頸,這時候就需要進行水平切分了。
水平切分也可以分為:水平分庫
和水平分表
。
1、水平分庫
水平分庫的原因
上面雖然已經把商品庫分成3個庫,但是隨着業務的增加一個訂單庫也出現QPS過高,數據庫響應速度來不及,一般mysql單機也就1000左右的QPS,如果超過1000就要考慮分庫。
如圖

2、水平分表
概念
一般我們一張表的數據不要超過1千萬,如果表數據超過1千萬,並且還在不斷增加數據,那就可以考慮分表。
如圖

3、垂直切分優缺點
優點
- 不存在單庫數據量過大、高並發的性能瓶頸,提升系統穩定性和負載能力
- 應用端改造較小,不需要拆分業務模塊
缺點
- 跨分片的事務一致性難以保證
- 跨庫的Join關聯查詢性能較差
- 數據多次擴展難度和維護量極大
三、數據分片規則
我們我們考慮去水平切分表,將一張表水平切分成多張表,這就涉及到數據分片的規則,比較常見的有:Hash取模分表
、數值Range分表
、一致性Hash算法分表
。
1、Hash取模分表
概念
一般采用Hash取模的切分方式,例如:假設按goods_id分4張表。(goods_id%4 取整確定表)

優點
- 數據分片相對比較均勻,不容易出現熱點和並發訪問的瓶頸。
缺點
- 后期分片集群擴容時,需要遷移舊的數據很難。
- 容易面臨跨分片查詢的復雜問題。比如上例中,如果頻繁用到的查詢條件中不帶goods_id時,將會導致無法定位數據庫,從而需要同時向4個庫發起查詢,
再在內存中合並數據,取最小集返回給應用,分庫反而成為拖累。
2、數值Range分表
概念
按照時間區間或ID區間來切分。例如:將goods_id為11000的記錄分到第一個表,10012000的分到第二個表,以此類推。
如圖

優點
- 單表大小可控
- 天然便於水平擴展,后期如果想對整個分片集群擴容時,只需要添加節點即可,無需對其他分片的數據進行遷移
- 使用分片字段進行范圍查找時,連續分片可快速定位分片進行快速查詢,有效避免跨分片查詢的問題。
缺點
- 熱點數據成為性能瓶頸。
例如按時間字段分片,有些分片存儲最近時間段內的數據,可能會被頻繁的讀寫,而有些分片存儲的歷史數據,則很少被查詢
3、一致性Hash算法
一致性Hash算法能很好的解決因為Hash取模而產生的分片集群擴容時,需要遷移舊的數據的難題
。至於具體原理這里就不詳細說,
可以參考一篇博客:一致性哈希算法(分庫分表,負載均衡等)
四、分庫分表帶來的問題
任何事情都有兩面性,分庫分表也不例外,如果采用分庫分表,會引入新的的問題
1、分布式事務問題
使用分布式事務中間件解決,具體是通過最終一致性還是強一致性分布式事務,看業務需求,這里就不多說。
2、跨節點關聯查詢 Join 問題
切分之前,我們可以通過Join來完成。而切分之后,數據可能分布在不同的節點上,此時Join帶來的問題就比較麻煩了,考慮到性能,盡量避免使用Join查詢。
解決這個問題的一些方法:
全局表
全局表,也可看做是 "數據字典表",就是系統中所有模塊都可能依賴的一些表,為了避免跨庫Join查詢,可以將 這類表在每個數據庫中都保存一份。這些數據通常
很少會進行修改,所以也不擔心一致性的問題。
字段冗余
利用空間換時間,為了性能而避免join查詢。例:訂單表保存userId時候,也將userName冗余保存一份,這樣查詢訂單詳情時就不需要再去查詢"買家user表"了。
數據組裝
在系統層面,分兩次查詢。第一次查詢的結果集中找出關聯數據id,然后根據id發起第二次請求得到關聯數據。最后將獲得到的數據進行字段拼裝。
3、跨節點分頁、排序、函數問題
跨節點多庫進行查詢時,會出現Limit分頁、Order by排序等問題。分頁需要按照指定字段進行排序,當排序字段就是分片字段時,通過分片規則就比較容易定位到指定的分片;
當排序字段非分片字段時,就變得比較復雜了。需要先在不同的分片節點中將數據進行排序並返回,然后將不同分片返回的結果集進行匯總和再次排序,最終返回給用戶。
4、全局主鍵避重問題
如果都用主鍵自增
肯定不合理,如果用UUID
那么無法做到根據主鍵排序,所以我們可以考慮通過雪花ID
來作為數據庫的主鍵,
有關雪花ID可以參考我之前寫的博客:靜態內部類單例模式實現雪花算法
5、數據遷移問題
采用雙寫的方式
,修改代碼,所有涉及到分庫分表的表的增、刪、改的代碼,都要對新庫進行增刪改。同時,再有一個數據抽取服務,不斷地從老庫抽數據,往新庫寫,
邊寫邊按時間比較數據是不是最新的。
參考
1、分庫分表
2、談談分庫分表吧?
我相信,無論今后的道路多么坎坷,只要抓住今天,遲早會在奮斗中嘗到人生的甘甜。抓住人生中的一分一秒,勝過虛度中的一月一年!(16)