咱們應該接觸過或者據說過數據庫的性能瓶頸問題。對於一個單機應用而言,提高數據庫性能的最快路徑就是氪金 - 買更高性能的數據庫服務器,只要錢到位,性能不是問題。git
可是當系統性能增長到必定地步時,你會發現,原先花 3000 塊提高了 50% 的性能,如今花 30000 塊,才提高了不到 10%。程序員
也就是說,咱們花了錢,但沒有獲得等價的性能提高,這個時候,咱們就要考慮數據庫的可擴展性了。github
要討論 MySQL 的可擴展性,就要先明確可擴展性的定義。在此以前,咱們先拋開 MySQL,專一於擴展性,搞清楚什麼是擴展性,才能更有針對性的去提高數據庫的擴展性。數據庫
咱們經常把「可擴展性」、「高可用性」以及「性能」用做同義詞,但事實上它們是徹底不一樣的。簡單來講,性能是響應時間,可用性是宕機時間,而擴展性代表了當須要增長資源以執行更多工做時,系統可以得到等價的性能提高的能力。換種說法,可擴展性就是咱們可以儘量的花費相同的資源提高等價的性能。而缺少擴展能力的系統在達到收益遞減的轉折點後,將沒法進一步增加。安全
容量是一個和可擴展性相關的概念。系統容量表示在必定時間內可以完成的工做量。服務器
容量和可擴展性並不依賴於性能。以高速公路上的汽車來類比的話:併發
在上面這個類比中,可擴展性依賴多個條件:換道設計是否合理、路上有多少車拋錨或發生事故、汽車行駛速度不一樣以及是否頻繁變換車道。但通常來講,和汽車的引擎是否強大無關。性能
這並非說性能不重要,性能確實重要,只是要注意的是,即便系統性能不是很高的系統也能夠具有可擴展性。優化
從較高層次看,可擴展性就是可以經過增長資源來提高容量的能力。網站
對於容量,咱們能夠簡單的認爲是處理負載的能力,而從不一樣的角度考慮負載對咱們優化擴展性頗有幫助。
數據量
應用所能累計的數據量是可擴展性最廣泛的挑戰,特別是對於如今的互聯網應用而言,由於從不刪除數據。
用戶量
首先,即便每一個用戶只有少許的數據,但在累計到必定數量的用戶後,數據量也會開始不成比例的增加,且速度快過用戶數增加。其次,更多的用戶意味着要處理更多的事務,而且事務數可能和用戶數不成比例。最後,大量用戶也意味着更多複雜的查詢。
用戶活躍度
不是全部的用戶活躍度都相同,而且用戶活躍度也不老是不變的。若是用戶忽然變得活躍,例如 github 給小團隊免費開放了私有化倉庫,那麼其對應的負載可能會明顯提高。要注意的是,用戶活躍度不只僅指頁面瀏覽數(PV),即便一樣的 PV,若是網站的某個須要執行大量查詢工做的功能變得更受歡迎,也可能致使更多的工做。
相關數據集的大小
若是用戶間存在關係,應用可能須要在整個相關聯用戶羣體上執行查詢和計算,這比處理一個個的用戶和用戶數據要複雜的多。
說了這麼多,只是爲了讓咱們更好的理解可擴展性的讓咱們用下面圖表來更明確的表達可擴展性。
假設有一個只有一臺服務器的系統,而且可以測量它的最大容量,如圖 1 所示:
假設咱們如今增長一臺服務器,系統的能力加倍,如圖 2 所示:
圖 2 就是線性擴展。咱們增長了一倍的服務器,增長了一倍的容量。然而,理想是美好的,現實是骨感的。大部分系統並非線性擴展的,而是如圖 3 所示的擴展方式:
大部分系統都只能以比線性擴展略低的擴展係數進行擴展。這就致使,多數系統最終會達到一個最大吞吐量臨界點,超過這個點後增長投入可能反而會下降系統的吞吐量。
到這一步,你們對擴展性應該已經有一個較爲清晰的概念了。在此基礎上,讓咱們再深刻一步:Amdahl 擴展 和 USL 擴展。
簡而言之,USL 說的是線下擴展的誤差可經過兩個因素來創建模型:
在對第一個因素繼續建模後,就有了著名的(聽過這個著名嗎?)阿姆達爾定律(Amdahl)。第一個因素最終會致使吞吐量趨於平緩。若是部分任務沒法並行,那麼無論你若是分而治之,該任務至少須要串行部分的時間。這句話很重要,讓咱們用一個栗子再簡單闡述下:
假設你們都作過韭菜煎蛋這道菜,咱們作這道菜時,有幾個必要步驟:
就上面 3 個步驟而言,你能夠在切韭菜的時候,讓你女票幫你打蛋液,也就是說 一、2 是能夠並行的,可是咱們能邊切菜邊煎嗎?或者邊打蛋液邊煎嗎?顯示是不行的。所以,步驟 3 和 一、2 是串行的。
這時候,咱們就會發現,作韭菜煎蛋這個任務須要的時間 t 爲:
t = MAX(t1, t2) + t3;
對第二個因素,須要交互的工做而言,交互就意味着內部節點間或者進程間的通訊。這種通訊的代價取決於通訊信道的數量,而信道的數量將按照系統內工做者數量的二次方增加,因此最終開銷比帶來的收益增加的更快,這就是產生擴展性倒退的緣由。由此和 Amdahl 定律,就得出了 USL。
圖 4 闡明瞭目前討論的三個概念:線性擴展、Amdahl 擴展以及 USL 擴展。而大多數真實系統看起來更像 USL 曲線。
至此,關於擴展性的概念描述告一段落。接下來,咱們回到正題,看看 MySQL 的擴展性如何規劃。
什麼狀況下須要擴展?,這是個值得咱們牢記的問題。當咱們提到系統的可擴展性時,通常只有兩種狀況:
上述兩種狀況,大多數狀況下咱們碰到的應該都是後者。具體表現爲:
若是是可擴展的應用,能夠簡單地增長更多的服務器來分擔負載。但若是是可擴展性比較差的,你就會發現 - 只剩下提升可擴展性這一條路可走。
只有一條路,那就且行且 996 吧!
走上了提高擴展性這條路,接下來的問題就是,如何提升可擴展性?這裏比較困難的部分是估算應用承擔的負載到底有多少?這個值不必定很是精確,但必須在必定的數量級範圍內。什麼?你問爲何要在必定範圍內?不清楚敵人的火力,我們是準備用高射炮打蚊子仍是用大刀對機槍呢?
除此以外,爲了能幫助咱們更好的規劃可擴展性,我們最好還能想清楚下面這個問題:
程序員們理想的開發環境應該是:計劃先行、有足夠可以一塊兒戰鬥的同伴、有花不完的預算等等。但現實是:
正常狀況下,提高系統的擴展性的難度可能要比重構的難度還要大。所以,在你沒有徹底把系統摸熟悉,或對擴展性還模糊的時候,千萬別給老闆說要提高系統的擴展性。
在老闆要求提高性能時,你要想盡一切辦法知足他提高性能的需求,同時,要多想下如何提升系統的擴展性,爲未來提高擴展性贏得時間。
能夠經過如下工做先提高系統性能: