數據庫的分庫分表思想,高級工程師必備

前言

關係型數據庫本身比較容易成爲系統瓶頸,單機存儲容量、連接數、處理能力都有限。當單表的數據量達到1000W或100G以後,由於查詢維 度較多,即使添加從庫、優化索引,做很多操作時性能仍下降嚴重。此時就要考慮對其進行切分了,切分的目的就在於減少數據庫的負擔,縮短查詢時間。

數據庫分佈式核心內容無非就是數據切分(Sharding),以及切分後對數據的定位、整合。數據切分就是將數據分散存儲到多個數據庫中,使得單一數據庫中的數據量變小,通過擴充主機的數量緩解單一數據庫的性能問題,從而達到提升數據庫操作性能的目的。數據切分根據其切分類型,可以分爲兩種方式:垂直(縱向)切分和水平(橫向)切分

正文

什麼是垂直(縱向)切分?

垂直切分常見有垂直分庫和垂直分表兩種。

垂直分庫就是根據業務耦合性,將關聯度低的不同表存儲在不同的數據庫。做法與大系統拆分爲多個小系統類似,按業務分類進行獨立劃分。與"微服務治理"的做法相似,每個微服務使用單獨的一個數據庫。如圖:

數據庫分庫分表,何時分?怎樣分?詳細解讀,一篇就夠了

 

垂直分表是基於數據庫中的"列"進行,某個表字段較多,可以新建一張擴展表,將不經常用或字段長度較大的字段拆分出去到擴展表中。在字段很多的情況下(例 如一個大表有100多個字段),通過"大表拆小表",更便於開發與維護,也能避免跨頁問題,MySQL底層是通過數據頁存儲的,一條記錄佔用空間過大會導 致跨頁,造成額外的性能開銷。另外數據庫以行爲單位將數據加載到內存中,這樣表中字段長度較短且訪問頻率較高,內存能加載更多的數據,命中率更高,減少了 磁盤IO,從而提升了數據庫性能。

垂直切分的優點:

  • 解決業務系統層面的耦合,業務清晰
  • 與微服務的治理類似,也能對不同業務的數據進行分級管理、維護、監控、擴展等
  • 高併發場景下,垂直切分一定程度的提升IO、數據庫連接數、單機硬件資源的瓶頸

缺點:

  • 部分表無法join,只能通過接口聚合方式解決,提升了開發的複雜度
  • 分佈式事務處理複雜
  • 依然存在單表數據量過大的問題(需要水平切分)

什麼是水平(橫向)切分?

當一個應用難以再細粒度的垂直切分,或切分後數據量行數巨大,存在單庫讀寫、存儲性能瓶頸,這時候就需要進行水平切分了。

水平切分分爲庫內分表和分庫分表,是根據表內數據內在的邏輯關係,將同一個表按不同的條件分散到多個數據庫或多個表中,每個表中只包含一部分數據,從而使得單個表的數據量變小,達到分佈式的效果。如圖所示:

數據庫分庫分表,何時分?怎樣分?詳細解讀,一篇就夠了

 

庫內分表只解決了單一表數據量過大的問題,但沒有將表分佈到不同機器的庫上,因此對於減輕MySQL數據庫的壓力來說,幫助不是很大,大家還是競爭同一個物理機的CPU、內存、網絡IO,最好通過分庫分表來解決。

水平切分的優點:

  • 不存在單庫數據量過大、高併發的性能瓶頸,提升系統穩定性和負載能力
  • 應用端改造較小,不需要拆分業務模塊

缺點:

  • 跨分片的事務一致性難以保證
  • 跨庫的join關聯查詢性能較差
  • 數據多次擴展難度和維護量極大

水平切分後同一張表會出現在多個數據庫/表中,每個庫/表的內容不同。

什麼時候考慮切分

1、能不切分儘量不要切分

並不是所有表都需要進行切分,主要還是看數據的增長速度。切分後會在某種程度上提升業務的複雜度,數據庫除了承載數據的存儲和查詢外,協助業務更好的實現需求也是其重要工作之一。

不到萬不得已不用輕易使用分庫分表這個大招,避免"過度設計"和"過早優化"。分庫分表之前,不要爲分而分,先盡力去做力所能及的事情,例如:升級硬件、升級網絡、讀寫分離、索引優化等等。當數據量達到單表的瓶頸時候,再考慮分庫分表。

2、數據量過大,正常運維影響業務訪問

這裏說的運維,指:

1)對數據庫備份,如果單表太大,備份時需要大量的磁盤IO和網絡IO。例如1T的數據,網絡傳輸佔50MB時候,需要20000秒才能傳輸完畢,整個過程的風險都是比較高的

2)對一個很大的表進行DDL修改時,MySQL會鎖住全表,這個時間會很長,這段時間業務不能訪問此表,影響很大。如果使用pt- online-schema-change,使用過程中會創建觸發器和影子表,也需要很長的時間。在此操作過程中,都算爲風險時間。將數據表拆分,總量減 少,有助於降低這個風險。

3)大表會經常訪問與更新,就更有可能出現鎖等待。將數據切分,用空間換時間,變相降低訪問壓力

3、隨着業務發展,需要對某些字段垂直拆分

4、數據量快速增長

隨着業務的快速發展,單表中的數據量會持續增長,當性能接近瓶頸時,就需要考慮水平切分,做分庫分表了。此時一定要選擇合適的切分規則,提前預估好數據容量

5、安全性和可用性

雞蛋不要放在一個籃子裏。在業務層面上垂直切分,將不相關的業務的數據庫分隔,因爲每個業務的數據量、訪問量都不同,不能因爲一個業務把數 據庫搞掛而牽連到其他業務。利用水平切分,當一個數據庫出現問題時,不會影響到100%的用戶,每個庫只承擔業務的一部分數據,這樣整體的可用性就能提 高。

案例分析

1、用戶中心業務場景

用戶中心是一個非常常見的業務,主要提供用戶註冊、登錄、查詢/修改等功能,其核心表爲:

數據庫分庫分表,何時分?怎樣分?詳細解讀,一篇就夠了

 

任何脫離業務的架構設計都是耍流氓,在進行分庫分表前,需要對業務場景需求進行梳理:

  • 用戶側:前臺訪問,訪問量較大,需要保證高可用和高一致性。主要有兩類需求:
  • 用戶登錄:通過login_name/phone/email查詢用戶信息,1%請求屬於這種類型
  • 用戶信息查詢:登錄之後,通過uid來查詢用戶信息,99%請求屬這種類型
  • 運營側:後臺訪問,支持運營需求,按照年齡、性別、登陸時間、註冊時間等進行分頁的查詢。是內部系統,訪問量較低,對可用性、一致性的要求不高。

2、水平切分方法

當數據量越來越大時,需要對數據庫進行水平切分,上文描述的切分方法有"根據數值範圍"和"根據數值取模"。

"根據數值範圍":以主鍵uid爲劃分依據,按uid的範圍將數據水平切分到多個數據庫上。例如:user-db1存儲uid範圍爲0~1000w的數據,user-db2存儲uid範圍爲1000w~2000wuid數據。

  • 優點是:擴容簡單,如果容量不夠,只要增加新db即可。
  • 不足是:請求量不均勻,一般新註冊的用戶活躍度會比較高,所以新的user-db2會比user-db1負載高,導致服務器利用率不平衡

"根據數值取模":也是以主鍵uid爲劃分依據,按uid取模的值將數據水平切分到多個數據庫上。例如:user-db1存儲uid取模得1的數據,user-db2存儲uid取模得0的uid數據。

  • 優點是:數據量和請求量分佈均均勻
  • 不足是:擴容麻煩,當容量不夠時,新增加db,需要rehash。需要考慮對數據進行平滑的遷移。

3、非uid的查詢方法

水平切分後,對於按uid查詢的需求能很好的滿足,可以直接路由到具體數據庫。而按非uid的查詢,例如login_name,就不知道具體該訪問哪個庫了,此時需要遍歷所有庫,性能會降低很多。

對於用戶側,可以採用"建立非uid屬性到uid的映射關係"的方案;對於運營側,可以採用"前臺與後臺分離"的方案。

3.1、建立非uid屬性到uid的映射關係

1)映射關係

例如:login_name不能直接定位到數據庫,可以建立login_name→uid的映射關係,用索引表或緩存來存儲。當訪問login_name時,先通過映射表查詢出login_name對應的uid,再通過uid定位到具體的庫。

映射表只有兩列,可以承載很多數據,當數據量過大時,也可以對映射表再做水平切分。這類kv格式的索引結構,可以很好的使用cache來優化查詢性能,而且映射關係不會頻繁變更,緩存命中率會很高。

3.2、前臺與後臺分離

對於用戶側,主要需求是以單行查詢爲主,需要建立login_name/phone/email到uid的映射關係,可以解決這些字段的查詢問題。

而對於運營側,很多批量分頁且條件多樣的查詢,這類查詢計算量大,返回數據量大,對數據庫的性能消耗較高。此時,如果和用戶側公用同一批服務或數據庫,可能因爲後臺的少量請求,佔用大量數據庫資源,而導致用戶側訪問性能降低或超時。

這類業務最好採用"前臺與後臺分離"的方案,運營側後臺業務抽取獨立的service和db,解決和前臺業務系統的耦合。由於運營側對可用 性、一致性的要求不高,可以不訪問實時庫,而是通過binlog異步同步數據到運營庫進行訪問。在數據量很大的情況下,還可以使用ES搜索引擎或Hive 來滿足後臺複雜的查詢方式。

最後

謝謝大家的閱讀,原創不易,喜歡就點個贊,這將是我最強的寫作動力。如果你覺得文章對你有所幫助,也蠻有趣的,就關注一下我的博客,謝謝。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章