MySQL_分庫分表

分庫分表

數據切分

  經過某種特定的條件,將咱們存放在同一個數據庫中的數據分散存放到多個數據庫(主機)上面,以達到分散單臺設備負載的效果。數據的切分同時還可以提升系統的整體可用性,因爲單臺設備Crash以後,僅僅有整體數據的某一部分不可用,而不是所有的數據。html

切分模式

  數據的切分(Sharding)依據其切分規則的類型。可以分爲兩種切分模式。前端

  依照不一樣的表(或者Schema)來切分到不一樣的數據庫(主機)之上,這樣的切可以稱之爲數據的垂直(縱向)切分。另一種則是依據表中的數據的邏輯關係,將同一個表中的數據依照某種條件拆分到多臺數據庫(主機)上面。這樣的切分稱之爲數據的水平(橫向)切分。mysql

  當咱們某個(或者某些)表的數據量和訪問量特別的大,經過垂直切分將其放在獨立的設備上後仍然沒法知足性能要求,這時候咱們就必須將垂直切分和水平切分相結合。先垂直切分,而後再水平切分。才幹解決這樣的超大型表的性能問題。sql

垂直切分

  垂直切分的最大特色就是規則簡單,實施也更爲方便,尤爲適合各業務之間的耦合度很是低。相互影響很是小,業務邏輯很是清晰的系統。在這樣的系統中,可以很是easy作到將不一樣業務模塊所使用的表分拆到不一樣的數據庫中。依據不一樣的表來進行拆分。對應用程序的影響也更小,拆分規則也會比較簡單清晰。數據庫

  數據的垂直切分。也可以稱之爲縱向切分。將數據庫想象成爲由很是多個一大塊一大塊的「數據塊」(表)組成。咱們垂直的將這些「數據塊」切開,而後將他們分散到多臺數據庫主機上面。這樣的切分方法就是一個垂直(縱向)的數據切分。後端

  一個架構設計較好的應用系統。其整體功能確定是由很是多個功能模塊所組成的。而每個功能模塊所需要的數據對應到數據庫中就是一個或者多個表。架構

  而在架構設計中,各個功能模塊相互之間的交互點越統1、越少,系統的耦合度就越低,系統各個模塊的維護性以及擴展性也就越好。這樣的系統。實現數據的垂直切分也就越容易。負載均衡

  當咱們的功能模塊越清晰,耦合度越低,數據垂直切分的規則定義也就越簡單。全然可以依據功能模塊來進行數據的切分,不一樣功能模塊的數據存放於不一樣的數據庫主機中,可以很是easy就避免掉跨數據庫的Join存在。同一時候系統架構也很是的清晰。分佈式

  固然,不多有系統可以作到所有功能模塊所使用的表全然獨立,全然不須要訪問對方的表或者須要兩個模塊的表進行Join操做。這樣的狀況下,咱們就必須依據實際的應用場景進行評估權衡。決定是遷就應用程序將須要Join的表的相關模塊都存放在同一個數據庫中,仍是讓應用程序作不少其它的事情,也就是程序全然經過模塊接口取得不一樣數據庫中的數據,而後在程序中完成Join操做。模塊化

  通常來講,假設是一個負載相對不是很是大的系統,而且表關聯又很是的頻繁。那可能數據庫讓步,將幾個相關模塊合併在一塊兒下降應用程序工做的方案可以下降較多的工做量,是一個可行的方案。

  固然,經過數據庫的讓步,讓多個模塊集中共用數據源,實際上也是默許了各模塊架構耦合度增大的發展,可能會讓之後的架構愈來愈惡化。尤爲是當發展到必定階段以後,發現數據庫實在沒法承擔這些表所帶來的壓力。不得不面臨再次切分的時候。所帶來的架構改形成本可能會遠遠大於最初的時候。

  因此,在數據庫進行垂直切分的時候,怎樣切分,切分到什麼樣的程度,是一個比較考驗人的難題。只能在實際的應用場景中經過平衡各方面的成本和收益,才能分析出一個真正適合本身的拆分方案。

  如今咱們使用example數據庫,簡單的分析一下。而後再設計一個簡單的切分規則,進行一次垂直垂直拆分。

  系統功能可以基本分爲四個功能模塊:用戶、羣組消息、相冊以及事件。分別對應爲例如如下這些表:

  • 用戶模塊表:user、user_profile、user_group、user_photo_album
  • 羣組討論表:groups、group_message、group_message_content、top_message
  • 相冊相關表:photo、photo_album、photo_album_relation、photo_comment
  • 事件信息表:event

  初略一看,沒有哪個模塊可以脫離其它模塊獨立存在,模塊與模塊之間都存在着關係。莫非沒法切分?

  固然不是,咱們再略微深刻分析一下,可以發現,儘管各個模塊所使用的表之間都有關聯,但是關聯關係還算比較清晰,也比較簡單。

  • 羣組討論模塊和用戶模塊之間主要存在經過用戶或者是羣組關係來進行關聯。通常關聯的時候都會是經過用戶的id或者nick_name以及group的id來進行關聯。經過模塊之間的接口實現不會帶來太多麻煩。
  • 相冊模塊僅僅與用戶模塊存在經過用戶的關聯。這兩個模塊之間的關聯基本就有經過用戶id關聯的內容。簡單清晰,接口明白;
  • 事件模塊與各個模塊可能都有關聯,但是都僅僅關注其各個模塊中對象的ID信息,可以作到很是簡單的分拆。

  因此,咱們第一步可以將數據庫依照功能模塊相關的表進行一次垂直拆分。每個模塊所涉及的表單獨到一個數據庫中,模塊與模塊之間的表關聯都在應用系統端經過接口來處理。例如如下圖所看到的:

  經過這樣的垂直切分以後,以前僅僅能經過一個數據庫來提供的服務,就被分拆成四個數據庫來提供服務,服務能力天然是增長几倍。

  垂直切分的優勢

    • 數據庫的拆分簡單明瞭,拆分規則明白;
    • 應用程序模塊清晰明白,整合簡單;
    • 數據維護方便易行,容易定位。

  垂直切分的缺點:

    •  部分表關聯沒法在數據庫級別完畢,需要在程序中完畢;
    • 對於訪問極其頻繁且數據量超大的表仍然存在性能瓶頸,不必定能知足要求;
    • 事務處理相對更爲複雜;
    • 切分達到必定程度以後,擴展性會遇到限制;
    • 過分切分可能會帶來系統過分複雜而難以維護。

  針對於垂直切分可能遇到數據切分及事務問題,在數據庫層面很難找到一個較好的處理方案。實際應用案例中,數據庫的垂直切分大可能是與應用系統的模塊相對應,同一個模塊的數據源存放於同一個數據庫中,可以解決模塊內部的數據關聯問題。而模塊與模塊之間,則經過應用程序以服務接口方式來相互提供所需要的數據。

  儘管這樣作在數據庫的整體操做次數方面確實會有所添加,但是在系統整體擴展性以及架構模塊化方面,都是故意的。可能在某些操做的單次響應時間會稍有添加。但是系統的整體性能很是可能反而會有必定的提高。而擴展瓶頸問題,就只能依靠數據水平切分架構來解決了。

數據的水平切分

  水平切分與垂直切分相比, 相對來講略微複雜一些。因爲要將同一個表中的不一樣數據拆分到不一樣的數據庫中,對於應用程序來講,拆分規則自己就較依據表名來拆分更爲複雜,後期的數據維護也會更爲複雜一些。

  數據的垂直切分基本上可以簡單的理解爲依照表依照模塊來切分數據,而水平切分就再也不是依照表或者是功能模塊來切分了。通常來講,簡單的水平切分主要是將某個訪問極其平庸的表再依照某個字段的某種規則來分散到多個表之中,每個表中包括一部分數據。

  簡單來講,咱們可以將數據的水平切分理解爲是依照數據行的切分。就是將表中的某些行切分到一個數據庫,而另外的某些行又切分到其它的數據庫中。固然,爲了可以比較容易的斷定各行數據被切分到哪一個數據庫中了,切分老是都需要依照某種特定的規則來進行的。

  如依據某個數字類型字段基於特定數目取模,某個時間類型字段的範圍。或者是某個字符類型字段的hash值。假設整個系統中大部分核心表都可以經過某個字段來進行關聯。那這個字段天然是一個進行水平分區的上上之選了,固然,很是特殊沒法使用就僅僅能另選其它了。

  通常來講,像現在互聯網很是火爆的Web2.0類型的站點。基本上大部分數據都可以經過會員用戶信息關聯上,可能很是多核心表都很是適合經過會員ID來進行數據的水平切分。

  而像論壇社區討論系統,就更容易切分了,很是簡單,依照論壇編號來進行數據的水平切分。

  切分以後基本上不會出現各個庫之間的交互。

  如咱們的演示樣例系統。所有數據都是和用戶關聯的。那麼咱們就可以依據用戶來進行水平拆分,將不一樣用戶的數據切分到不一樣的數據庫中。固然,惟一有點差異的是用戶模塊中的groups表和用戶沒有直接關係。因此groups不能依據用戶來進行水平拆分。對於這樣的特殊狀況下的表,咱們全然可以獨立出來。單獨放在一個獨立的數據庫中。事實上這個作法可以說是用了「數據的垂直切分」方法。

  因此,對於咱們的演示樣例數據庫來講,大部分的表都可以依據用戶ID來進行水平的切分。不一樣用戶相關的數據進行切分以後存放在不一樣的數據庫中。如將所有用戶ID經過2取模而後分別存放於兩個不一樣的數據庫中。

  每個和用戶ID關聯上的表都可以這樣切分。這樣,基本上每個用戶相關的數據。都在同一個數據庫中,即便是需要關聯,也可以很是簡單的關聯上。

  咱們可以經過下圖來更爲直觀的展現水平切分相關信息:

  水平切分的長處

    • 表關聯基本可以在數據庫端所有完畢;
    • 不會存在某些超大型數據量和高負載的表遇到瓶頸的問題;
    • 應用程序端整體架構修改相對較少;
    • 事務處理相對簡單;
    • 只要切分規則定義好,基本上不會遇到擴展性限制;

  水平切分的缺點

    •  切分規則相對更爲複雜,很難抽象出一個可以知足整個數據庫的切分規則;
    • 後期數據的維護難度有所增長,人爲手工定位數據更困難;
    • 應用系統各模塊耦合度較高,可能會對後面數據的遷移拆分形成必定的困難。

 組合切分

   通常來講,咱們數據庫中的所有表很是難經過某一個(或少數幾個)字段所有關聯起來,因此很是難簡單的只經過數據的水平切分來解決所有問題。而垂直切分也僅僅能解決部分問題,對於那些負載很是高的系統,即便只是單個表都沒法經過單臺數據庫主機來承擔其負載。

  咱們必須結合「垂直」和「水平」兩種切分方式同一時候使用,充分利用二者的長處,避開其缺點。

  每個應用系統的負載都是一步一步增加上來的,在開始遇到性能瓶頸的時候,大多數架構師和DBA都會選擇先進行數據的垂直拆分,因爲這樣的成本最小,最符合這個時期所追求的最大投入產出比。然而,隨着業務的不斷擴張,系統負載的持續增加,在系統穩定一段時期以後,通過了垂直拆分以後的數據庫集羣可能又再一次不堪重負,遇到了性能瓶頸。

  這時候咱們該怎樣抉擇?是再次進一步細分模塊呢,仍是尋求其它的辦法來解決?假設咱們再一次像最開始那樣繼續細分模塊,進行數據的垂直切分,那咱們可能在不久的未來,又會遇到現在所面對的相同的問題。而且隨着模塊的不斷的細化,應用系統的架構也會愈來愈複雜,整個系統很是可能會出現失控的局面。

  這時候咱們就必需要經過數據的水平切分的優點,來解決這裏所遇到的問題。而且,咱們全然不須要在使用數據水平切分的時候,推倒以前進行數據垂直切分的成果,而是在其基礎上利用水平切分的優點來避開垂直切分的弊端,解決系統複雜性不斷擴大的問題。

  而水平拆分的弊端(規則難以統一)也已經被以前的垂直切分解決掉了。讓水平拆分可以進行的駕輕就熟。

   對於咱們的演示樣例數據庫,假設在最開始,咱們進行了數據的垂直切分,然而隨着業務的不斷增加,數據庫系統遇到了瓶頸,咱們選擇重構數據庫集羣的架構。怎樣重構?考慮到以前已經作好了數據的垂直切分,而且模塊結構清晰明白。

   而業務增加的勢頭愈來愈猛。即便現在進一步再次拆分模塊,也堅持不了過久。

  咱們選擇了在垂直切分的基礎上再進行水平拆分。

  在經歷過垂直拆分後的各個數據庫集羣中的每個都僅僅有一個功能模塊。而每個功能模塊中的所有表基本上都會與某個字段進行關聯。如用戶模塊所有都可以經過用戶ID進行切分,羣組討論模塊則都經過羣組ID來切分。相冊模塊則依據相冊ID來進切分。最後的事件通知信息表考慮到數據的時限性(只會訪問近期某個時間段的信息),則考慮按時間來切分。

   下圖展現了切分後的整個架構:

  實際上,在很是多大型的應用系統中,垂直切分和水平切分這兩種數據的切分方法基本上都是並存的。而且常常在不斷的交替進行,以不斷的添加系統的擴展能力。咱們在應對不一樣的應用場景的時候,也需要充分考慮到這兩種切分方法各自的侷限,以及各自的優點。在不一樣的時期(負載壓力)使用不一樣的結合方式。

  聯合切分的長處

    •  可以充分利用垂直切分和水平切分各自的優點而避免各自的缺陷;
    •  讓系統擴展性獲得最大化提高。

   聯合切分的缺點

    •  數據庫系統架構比較複雜。維護難度更大。
    • 應用程序架構也相對更復雜。

數據切分及整合方案

   數據庫中的數據在通過垂直和(或)水平切分被存放在不一樣的數據庫主機以後,應用系統面臨的最大問題就是怎樣來讓這些數據源獲得較好的整合。接下來咱們主要分析可以使用的各類可以幫助咱們實現數據切分以及數據整合的整體解決方式。

  數據的整合很是難依靠數據庫自己來達到這個效果,儘管MySQL存在Federated存儲引擎,可以解決部分類似的問題。但是在實際應用場景中卻很是難較好的運用。那咱們該怎樣來整合這些分散在各個MySQL主機上面的數據源呢?

  總的來講,存在兩種解決思路:

    1. 在每個應用程序模塊中配置管理本身需要的一個(或者多個)數據源。直接訪問各個數據庫,在模塊內完畢數據的整合;
    2. 經過中間代理層來統一管理所有的數據源。後端數據庫集羣對前端應用程序透明;

  可能大多數人在面對上面這兩種解決思路的時候都會傾向於選擇第二種,尤爲是系統不斷變得龐大複雜的時候。

  確實,這是一個很是正確的選擇,儘管短時間內需要付出的成本可能會相對更大一些,但是對整個系統的擴展性來講,是很是有幫助的。

  因此,這裏重點分析一下在第二種解決思路中的一些解決方式。

自行開發中間代理層

  在決定選擇經過數據庫的中間代理層來解決數據源整合的架構方向以後,有很多公司(或者企業)選擇了經過自行開發符合自身應用特定場景的代理層應用程序。

  經過自行開發中間代理層可以最大程度的應對自身應用的特色。最大化的定製很是多個性化需求,在面對變化的時候也可以靈活的應對。這應該說是自行開發代理層最大的優點了。

  固然,選擇自行開發,享受讓個性化定製最大化的樂趣的同時,天然也需要投入不少其它的成原本進行前期研發以及後期的持續升級改進工做。而且自己的技術門檻可能也比簡單的Web應用要更高一些。因此,在決定選擇自行開發以前,仍是需要進行比較全面的評估爲好。

  因爲自行開發不少時候考慮的是怎樣更好的適應自身應用系統,應對自身的業務場景,因此這裏也很差分析太多。

利用MySQLProxy實現數據切分及整合

  MySQLProxy是MySQL官方提供的一個數據庫代理層產品,和MySQLServer同樣,相同是一個基於GPL開源協議的開源產品。可用來監視、分析或者傳輸他們之間的通信信息。他的靈活性使得你能夠最大限度的使用它,眼下具有的功能主要有鏈接路由,Query分析,Query過濾和修改,負載均衡。以及主要的HA機制等。

  實際上,MySQLProxy自己並不具備上述所有的這些功能。而是提供了實現上述功能的基礎。

  要實現這些功能,還需要經過自行編寫LUA腳原本實現。

  MySQLProxy其實是在Client請求與MySQLServer之間創建了一個鏈接池。所有Client請求都是發向MySQLProxy,而後經由MySQLProxy進行對應的分析。推斷出是讀操做仍是寫操做,分發至對應的MySQLServer上。對於多節點Slave集羣,也可以起作到負載均衡的效果。如下是MySQLProxy的基本架構圖:

  經過上面的架構簡圖,咱們可以很是清晰的看出MySQLProxy在實際應用中所處的位置,以及能作的基本事情。

  關於MySQLProxy更爲具體的實施細則在MySQL官方文檔中有很是具體的介紹和演示樣例。

數據切分與整合可能存在的問題

   在實施數據切分方案以前,咱們可能遇到的問題主要會有如下幾點:

引入分佈式事務的問題

  一旦數據進行切分被分別存放在多個MySQLServer中以後,不管咱們的切分規則設計的多麼的完美(實際上並不存在完美的切分規則),均可能形成以前的某些事務所涉及到的數據已經不在同一個MySQLServer中了。

  在這樣的場景下,假設咱們的應用程序仍然依照老的解決方式。那麼勢必需要引入分佈式事務來解決。而在MySQL各個版本號中,僅僅有從MySQL5.0開始之後的各個版本號纔開始對分佈式事務提供支持,而且眼下僅有Innodb提供分佈式事務支持。不只如此。即便咱們恰好使用了支持分佈式事務的MySQL版本號。同時也是使用的Innodb存儲引擎,分佈式事務自己對於系統資源的消耗是很是大的,性能自己也並不是過高,而且引入分佈式事務自己在異常處理方面就會帶來較多比較難控制的因素。

  事實上咱們可以可以經過一個變通的方法來解決這樣的問題。首先需要考慮的一件事情就是:是否數據庫是惟一一個可以解決事務的地方呢?事實上並不是這樣的,咱們徹底能夠結合數據庫以及應用程序二者來共同解決。各個數據庫解決本身身上的事務,而後經過應用程序來控制多個數據庫上面的事務。

  只要咱們願意,徹底可以將一個跨多個數據庫的分佈式事務分拆成多個僅處於單個數據庫上面的小事務。並經過應用程序來總控各個小事務。

  固然,這樣做的要求就是應用程序必需要有足夠的健壯性。固然也會給應用程序帶來一些技術難度。

跨節點Join的問題

  數據切分以後,可能會形成有些老的 Join 語句沒法繼續使用。因爲Join使用的數據源可能被切分到多個 MySQLServer 中了。

  這個問題從MySQL數據庫角度來看,假設必需要在數據庫端來直接解決的話,只能經過MySQL一種特殊的存儲引擎Federated來解決了。Federated存儲引擎是MySQL解決類似於Oracle的DBLink之類問題的解決方式。

  和 Oracle DBLink 的主要差異在於 Federated 會保存一份遠端表結構的定義信息在本地。咋一看,Federated確實是解決跨節點Join很是好的解決方式。但是咱們還應該清晰一點,假設遠端的表結構發生了變動,本地的表定義信息是不會跟着發生對應變化的。假設在更新遠端表結構的時候並無更新本地的Federated表定義信息,就可能形成Query執行出錯,沒法獲得正確的結果。

  針對這類問題,主要經過應用程序來進行處理,先在驅動表所在的 MySQLServer 中取出對應的驅動結果集,而後依據驅動結果集再到被驅動表所在的 MySQLServer 中取出對應的數據,可是會對性能有必定的負面影響。但是除了此法,基本上沒有太多其它更好的解決的方法了。

  而且,因爲數據庫經過較好的擴展以後,每臺 MySQLServer 的負載就可以獲得較好的控制。單純針對單條Query來講,其響應時間可能比不切分以前要提升一些,因此性能方面所帶來的負面影響也並不是太大。更況且,像這樣的須要跨節點Join的需求也並不是太多。相對於整體性能而言,可能也僅僅是很是小一部分而已。因此爲了整體性能的考慮,偶爾犧牲那麼一點點,事實上是值得的。畢竟系統優化自己就是存在很是多取捨和平衡的過程。

跨節點合併排序分頁問題

  一旦進行了數據的水平切分以後,可能就並不僅有跨節點Join沒法正常執行,有些排序分頁的Query語句的數據源可能也會被切分到多個節點。這樣形成的直接後果就是這些排序分頁Query沒法繼續正常執行。這和跨節點Join是一個道理。數據源存在於多個節點上,要經過一個Query來解決,就和跨節點Join是同樣的操做。相同Federated也可以部分解決。固然存在的風險也同樣。

  怎樣解決?解決的思路大致上和跨節點Join的解決類似,但是有一點和跨節點Join不太同樣。Join很是多時候都有一個驅動與被驅動的關係。因此Join自己涉及到的多個表之間的數據讀取通常都會存在一個順序關係。但是排序分頁就不太同樣了,排序分頁的數據源基本上可以說是一個表(或者一個結果集)。自己並不存在一個順序關係,因此在從多個數據源取數據的過程是全然可以並行的。

  排序分頁數據的取數效率咱們可以作的比跨庫Join更高,帶來的性能損失相對的要更小,在有些狀況下可能比在原來未進行數據切分的數據庫中效率更高了。

  固然,不管是跨節點Join仍是跨節點排序分頁。都會使咱們的應用server消耗不少其它的資源,尤爲是內存資源,因爲咱們在讀取訪問以及合併結果集的這個過程需要比原來處理不少其它的數據。

  經過數據切分技術將一個大的 MySQL Server 切分紅多個小的 MySQL Server ,既攻克了寫入性能瓶頸問題,同時也再一次提高了整個數據庫集羣的擴展性。不管是經過垂直切分,仍是水平切分。都可以讓系統遇到瓶頸的可能性更小。尤爲是當咱們使用垂直和水平相結合的切分方法以後,理論上將不會再遇到擴展瓶頸了。

 

參考鏈接

mysql數據庫分庫分表(Sharding)

相關文章
相關標籤/搜索