分佈式系統「伸縮性」大招之——「水平&垂直切分」詳解

若是第二次看到個人文章,歡迎 下方掃碼訂閱個人我的公衆號(跨界架構師)喲~
本文長度爲 5389字,建議閱讀 14分鐘。
堅持原創,每一篇都是用心之做~


沒想到這篇文章寫了這麼長,一時半會沒消化完的話,能夠收藏一下先。html

這是「伸縮性」章節的第四篇,先給新來的小夥伴們簡單回顧下前三篇的內容。node

作「伸縮性」最重要的就是先作好「無狀態」,如此才能夠爲所欲爲的進行橫向「擴展」,而不用擔憂在多個副本之間切換會產生錯亂。《分佈式系統關注點——「無狀態」詳解》聊的就是這個。程序員

不過,就算作好了橫向擴展,本質上仍是一個「大程序」,只是變得「可複製」了而已。算法

若是要消滅「大程序」,那就得「切分」,作好切分必然離不開「高內聚低耦合」的核心思想。《分佈式系統關注點——「高內聚低耦合」詳解》這篇聊的就是這個。數據庫

題外話:當你遇到單點單應用支撐不住使用的時候,Z哥給你的普適性建議是:先考慮「擴」,再考慮「切」。這個和寫代碼同樣,「增長」新功能每每比在老功能上改容易。

「擴」的話先考慮「垂直擴」(加硬件,錢能解決的都不是問題),再考慮「水平擴」(無狀態改造+多節點部署,這是小手術)。編程

「切」的話通常就是「垂直切」(根據業務切分,這是大手術),偶爾會用到「水平切」(其實就是單個應用裏的分層,好比先後端分離)。後端


第三篇《分佈式系統關注點——彈性架構》咱們聊了常見的兩種「鬆耦合」架構模式,爲的是讓應用程序的「伸縮性」更上一層樓。緩存

以上這些呢都是應用程序層面的工做。通常狀況下,在應用程序層面作作手術,再配合以緩存的充分運用,就能夠支撐系統發展很長時間了。特別是數據量不大,只是請求量大的「CPU密集型」場景。服務器

可是,若是所處的工做場景是一個很是成熟且具備必定規模的項目,愈加展到後面瓶頸老是出如今數據庫這裏。甚至會出現cpu長期高負荷、宕機等現象。微信

在如此場景下,就不得不對數據庫開刀了。此次Z哥就來和你聊聊作數據庫的「伸縮性」有哪些好方法。


核心訴求

面臨數據庫須要開刀的時候,整個系統每每已經長成這個樣子了。

圖片描述

正如前面所說,這時候的瓶頸每每會體如今「CPU」上。

由於對數據庫來講,硬盤和內存的擴容相對容易,由於它們均可以直接用「增長」的方式進行。

CPU就不一樣了,一旦CPU飆高,最多檢查下索引有沒有作好,完了以後基本就只能幹看着。

因此解決這個問題的思路天然就變成了:如何將一個數據庫的CPU壓力分攤到多個CPU上去。甚至能夠作到按需隨時增長。

那這不就是和應用程序同樣作「切分」嘛。也是分佈式系統的「分治」思想體現。

既然是切分,本質上就和應用程序同樣,也分爲「垂直切分」和「水平切分」。


垂直切分

垂直切分有時候也會被稱做「縱向切分」。

同應用程序同樣,它是以「業務」爲維度的切分方式,在不一樣的數據庫服務器上跑不一樣業務的數據庫,各司其職。


通常狀況下,Z哥建議你優先考慮「垂直切分」而不是「水平切分」,爲何呢?你能夠隨意打開手頭項目中的SQL語句看看,我想必然存在着大量的「join」和「transaction」關鍵字,這種關聯查詢和事務操做,本質上是一種「關係捆綁」,一旦面臨數據庫拆分以後,就無法玩了

此時你只有2個選擇。

  1. 要麼將沒必要要的「關係捆綁」邏輯捨棄掉,這須要在業務上做出調整,去除沒必要要的「批量操做」業務,或者去除沒必要要的強一致性事務。不過你也知道,確定有一些場景是去不完的。
  2. 要麼將「合併」,「關聯」等邏輯上浮,體現到業務邏輯層甚至是應用層的代碼中。

最終,無論怎麼選擇,改動起來都是一個大工程。


爲了讓這個工程儘量的動做小一些,追求更好的性價比,須要堅持一個原則——「避免拆分緊密關聯的表」。

由於兩個表之間關聯越緊密,意味着對「join」和「transaction」的需求越多,因此堅持這個原則可使得相同的模塊,緊密相關的業務都落在同一個庫中,這樣它們能夠繼續使用「join」和「transaction」來工做。

所以,咱們應當優先採用「垂直切分」的方式。


作「垂直切分」思路很簡單,通常狀況下,建議是與切分後的應用程序一一對應就好,不用多也不用少

圖片描述

實際工做中,要作好「垂直切分」主要體如今「業務」的熟悉度上,因此這裏就不繼續展開了。


「垂直切分」的優勢是:

  1. 高內聚,拆分規則清晰。相比「水平切分」數據冗餘度更低。
  2. 與應用程序是1:1的關係,方便維護和定位問題。一旦某個數據庫中發現異常數據,排查這個數據庫的關聯程序就好了。

可是這並非一個「一勞永逸」的方案,由於沒人能預料到將來業務會發展的怎麼樣,因此最明顯的缺點就是:對於訪問極其頻繁或者數據量超大的表仍然存在性能瓶頸。

確實須要解決這個問題的話,就須要搬出「水平切分」了。

題外話:不到迫不得己,儘可能避免進行「水平切分」。看完接下去的內容你就知道緣由了。


下面Z哥就給你好好聊聊「水平切分」,這纔是本文的重點。


水平切分

想象一下,在你作了「垂直切分」以後,仍是在某個數據庫中發現了一張數據量超過10億條的表。

這個時候要對這個表作「水平切分」,你會怎麼思考這個事情?


Z哥教給你的思路是:

  1. 先找到「最高頻「的「讀」字段
  2. 再看這個字段的實際使用中有什麼特色(批量查詢多仍是單個查詢多,是否同時是其它表的關聯字段等等)。
  3. 再根據這個特色選擇合適的切分方案。

爲何要先找到高頻的「讀」字段呢?

由於在實際的使用中,「讀」操做每每是遠大於「寫」操做的。通常進行「寫」以前都得經過「讀」來作先行校驗,然而「讀」還有本身單獨的使用場景。因此針對更高頻的「讀」場景去考慮,產生的價值必然也更大

好比,如今那張10億數據量的表是一張訂單表,結構是這樣:

order (orderId long, createTime datetime, userId long)

下面咱們先來看看有哪幾種「水平切分」的方式,完了才能明白什麼樣的場景適合哪一種方式。


範圍切分

這是一種「連續式」的切分方式。

好比根據時間(createTime)切分的話,咱們能夠按年月來分,order_201901一個庫,order_201902一個庫,以此類推。

根據順序數(orderId)切分的話,能夠100000~199999一個庫,200000~299999一個庫,以此類推。

這種切分法的優勢是:單個表的大小可控,擴展的時候無需數據遷移

缺點也很明顯,通常來講時間越近或者序號越大的數據越「新」,所以被訪問的頻率和機率相比「老」數據更多。會致使壓力主要集中在新的庫中,而歷史越久的庫,越空閒


Hash切分

與「範圍切分」正好相反,這是一種「離散式」的切分方式。

它的優勢就是解決了「範圍切分」的缺點,新數據被分散到了各個節點中,避免了壓力集中在少數節點上

一樣,缺點與「範圍切分」的優勢相反,一旦進行二次擴展,必然會涉及到數據遷移。由於Hash算法是固定的,算法一變,數據分佈就變了。

大多數狀況下,咱們的hash算法能夠經過簡單的「取模」運算來進行便可。就像下面這樣:

假如分紅11個庫的話,公式就是 orderId % 10。

100000 % 10 = 0,分配到db0。
100001 % 10 = 1,分配到db1。

....

100010 % 10 = 0,分配到db0。
100011 % 10 = 1,分配到db1。

其實,在某些場景下,咱們能夠經過自定義id的生成(能夠參考以前的文章,《分佈式系統中的必備良藥 —— 全局惟一單據號生成》)來作到既能夠經過hash切分來打散熱點數據,又能夠減小依賴全局表來定位具體的數據。

好比,在orderId中加入userId的尾數,以此達到orderId和userId取模結果相等的效果。仍是來舉個例子:

一個用戶的userId是200004,若是取一個4bit尾數的話,這裏就是4,用0100表示。

而後,咱們經過自定義id算法生成orderId的前60位,在後面補上0100。

因而,orderId % 10和 userId % 10的結果就是同樣的了。

固然,除了userId以外還想加入其餘的因子就很差使了。也就是,能夠在不增長全局表的狀況下,額外多支持1個維度。

提到了兩次全局表,那麼啥是全局表呢?


全局表

這種方式就是將用做切分依據的分區Key與對應的每一條具體數據的id保存到一個單獨的庫或者表中。例如要增長一張這樣的表:

nodeId orderId
01 100001
02 100002
01 100003
01 100004
...

如此一來,的確將大部分具體的數據分佈在了不一樣服務器上,可是這張全局表會給人一種「形散神不散」的感受。

由於請求數據的時候沒法直接定位須要的數據在哪臺服務器上,因此每一次操做都要先查詢一下這張全局表好知道具體的數據被存放在哪裏。

這種「中心化」的模式帶來的反作用就是瓶頸和風險轉移到了這張全局表上。可是,勝在邏輯簡單。


好了,那麼這幾種切分方案怎麼選擇呢?

Z哥給你的建議是,若是熱點數據不是特別集中的場景,建議先用「範圍切分」,不然選擇另外2種

選擇另外兩種的時候,數據量越大越傾向選擇Hash切分。由於後者在總體的可用性和性能上都比前者好,就是實現成本高一些。


「水平切分」真正作到了能夠「無限擴展」,可是也存在相應的弊端。

1)批量查詢、分頁等須要作更多的額外工做。特別是當一個表存在多個高頻字段用於where、order by或者group by的時候。

2)拆分規則不如「垂直切分」那麼明確。

因此仍是多說一句「廢話」:沒有完美的方案只有合適的方案,要結合具體的場景來選擇。(歡迎你在留言區提出你有疑惑的場景,和Z哥來討論討論)


如何實施

當你在具體實施「水平切分」的時候能夠在2個層面動刀,能夠是「表」層面,也能夠是「庫」層面。

在同一個數據庫下面分表,表名order_0 ,order_1, order_2.....。

它能夠解決單表數據過大,但並不能解決CPU負荷的問題。因此,當CPU並沒多少壓力,只是因爲表太大,致使執行SQL操做比較慢的話,能夠選擇這種方式。


這個時候表名能夠不變,都叫order,只是分紅10個庫。那麼就是db0-user db1-user db2-user......。

咱們前面大篇幅都是基於這個模式在聊,就很少說了。


表+庫

也能夠既分庫又分表,好比先分10個庫,而後每一個庫再分10張表。

這實際上是個二級索引的思路,經過庫來進行第一次定位,減小必定的資源消耗。

好比,先按年分庫,再按月分表。如此一來,若是須要獲取的數據只跨月但不跨年,咱們就能夠在單個庫內作聚合運算來完成,不涉及到跨庫操做。


不過,無論選擇哪一種方式來進行,你仍是會或多或少面臨如下兩個問題,逃不掉的。

  1. 跨庫join。
  2. 全局聚合或者排序操做。

解決第一個問題最佳方式仍是須要改變你的編程思惟。儘可能將一些邏輯、關係、約束等體如今應用程序的代碼中,避免由於方便而在SQL中作這些事情。

畢竟代碼是能夠寫成「無狀態」的,能夠隨時作擴展,可是SQL是跟着數據走的,而數據就是「狀態」,自然不利於擴展。

固然了,退而求其次,你也能夠冗餘大量的全局表來應對。只是如此一來,對「數據一致性」工做是個很大的考驗,另外,對存儲資源也是很大的開銷。

第二個問題的解決方案就是須要將本來的一次聚合或者一次排序變成兩次操做。其中的遍歷多個節點能夠以「並行」的方式進行。

圖片描述

那麼數據切分完以後程序如何來使用呢?這又能夠分爲兩種模式,「進程內」和「進程外」。

圖片描述

「進程內」的話,能夠在封裝好的DAL訪問框架中作,也能夠在ORM框架中作,還能夠在數據庫驅動中作。這個模式比較知名的解決方案如阿里的tddl。

圖片描述

「進程外」的話,就是代理模式,這個模式比較知名的解決方案是mycat、cobar、atlas等等,相對多一些,由於這種模式對應用程序是「低侵入」的,使用起來像「一個數據庫」。可是因爲多了一道網絡通訊,性能上會多一些損耗。


老規矩,下面再分享一些最佳實踐。


最佳實踐

首先分享兩個能夠不停機作數據切分的小竅門。咱們以實施hash法作水平切分的例子來看一下。

第一次作切分的時候,你能夠以「主-從」的形式將新增的節點做爲原始節點的副本,進行全量實時同步。

圖片描述

而後在這個基礎上刪除不屬於它的數據。(固然了,不刪也沒啥問題,就是多佔用一些空間)

圖片描述

這樣就能夠不用停機了。


第二,隨着時間的推移,若是後續支撐不住了,須要二次切分的話,咱們能夠選擇用2的倍數來擴展。

如此一來,數據的遷移變得很簡單,只須要作局部的遷移,和第一次作切分的思路是同樣的。

圖片描述

固然了,若是選擇的切分方式是「範圍切分」的話,就沒有二次切分時的困擾,數據天然跑到最新的節點上去了。好比咱們按年月分表的話。2019年3月的數據天然就落到了xxxx_201903的表中。


到這裏,Z哥仍是想特別強調的是,能不切分儘可能不要切分,能夠先使用「讀寫分離」之類的方案先來應對面臨的問題

若是實在要進行切分的話,務必先「垂直切分」,再考慮「水平切分」

通常來講,以這樣的順序來考慮,性價比更好。


總結

好了,咱們總結一下。

此次呢,Z哥先向你介紹了作數據庫切分的兩種思路。兩種思路通俗理解就是:「垂直拆分」等於「列」變「行」不變,「水平拆分」等於「行」變「列」不變。

而後着重聊了下「水平切分」的3種實現方式和具體實施的思路。

最後分享了一些實踐中的經驗給你。

但願對你有所啓發。




相關文章:

分佈式系統關注點——「無狀態」詳解

分佈式系統關注點——「高內聚低耦合」詳解

分佈式系統關注點——彈性架構

分佈式系統中的必備良藥 —— 全局惟一單據號生成



做者:Zachary

出處:https://www.cnblogs.com/Zacha...


若是你喜歡這篇文章,能夠點一下文末的「」。

這樣能夠給我一點反饋。: )

謝謝你的舉手之勞。

▶關於做者:張帆(Zachary, 我的微信號:Zachary-ZF)。堅持用心打磨每一篇高質量原創。歡迎掃描下方的二維碼~。

按期發表原創內容:架構設計丨分佈式系統丨產品丨運營丨一些思考

若是你是初級程序員,想提高但不知道如何下手。又或者作程序員多年,陷入了一些瓶頸想拓寬一下視野。歡迎關注個人公衆號「跨界架構師」,回覆「技術」,送你一份我長期收集和整理的思惟導圖。

若是你是運營,面對不斷變化的市場一籌莫展。又或者想了解主流的運營策略,以豐富本身的「倉庫」。歡迎關注個人公衆號「跨界架構師」,回覆「運營」,送你一份我長期收集和整理的思惟導圖。

圖片描述

相關文章
相關標籤/搜索