MySQL性能調優與架構設計——第 14 章 可擴展性設計之數據切分

第 14 章 可擴展性設計之數據切分前端

前言

經過 MySQL Replication 功能所實現的擴展老是會受到數據庫大小的限制,一旦數據庫過於龐大,尤爲是當寫入過於頻繁,很難由一臺主機支撐的時候,咱們仍是會面臨到擴展瓶頸。這時候,咱們就必須許找其餘技術手段來解決這個瓶頸,那就是咱們這一章所要介紹惡的數據切分技術。 數據庫

14.1 何謂數據切分後端

可能不少讀者朋友在網上或者雜誌上面都已經屢次見到關於數據切分的相關文章了,只不過在有些文章中稱之爲數據的 Sharding。其實無論是稱之爲數據的 Sharding 仍是數據的切分,其概念都是同樣的。簡單來講,就是指經過某種特定的條件,將咱們存放在同一個數據庫中的數據分散存放到多個數據庫(主機)上面,以達到分散單臺設備負載的效果。數據的切分同時還能夠提升系統的整體可用性,由於單臺設備 Crash 以後,只有整體數據的某部分不可用,而不是全部的數據。
數據的切分(Sharding)根據其切分規則的類型,能夠分爲兩種切分模式。一種是按照不一樣的表(或者 Schema)來切分到不一樣的數據庫(主機)之上,這種切能夠稱之爲數據的垂直(縱向)切分;另一種則是根據表中的數據的邏輯關係,將同一個表中的數據按照某種條件拆分到多臺數據庫(主機)上面,這種切分稱之爲數據的水平(橫向)切分。
垂直切分的最大特色就是規則簡單,實施也更爲方便,尤爲適合各業務之間的耦合度很是低,相互影響很小,業務邏輯很是清晰的系統。在這種系統中,能夠很容易作到將不一樣業務模塊所使用的表分拆到不一樣的數據庫中。根據不一樣的表來進行拆分,對應用程序的影響也更小,拆分規則也會比較簡單清晰。
水平切分於垂直切分相比,相對來講稍微複雜一些。由於要將同一個表中的不一樣數據拆分到不一樣的數據庫中,對於應用程序來講,拆分規則自己就較根據表名來拆分更爲複雜,後期的數據維護也會更爲複雜一些。
當咱們某個(或者某些)表的數據量和訪問量特別的大,經過垂直切分將其放在獨立的設備上後仍然沒法知足性能要求,這時候咱們就必須將垂直切分和水平切分相結合,先垂直切分,而後再水平切分,才能解決這種超大型表的性能問題。
下面咱們就針對垂直、水平以及組合切分這三種數據切分方式的架構實現及切分後數據的整合進行相應的分析。

14.2 數據的垂直切分 服務器

咱們先來看一下,數據的垂直切分究竟是如何一個切分法的。數據的垂直切分,也能夠稱之爲縱向切分。將數據庫想象成爲由不少個一大塊一大塊的「數據塊」(表)組成,咱們垂直的將這些「數據塊」切開,而後將他們分散到多臺數據庫主機上面。這樣的切分方法就是一個垂直(縱向)的數據切分。
一個架構設計較好的應用系統,其整體功能確定是由不少個功能模塊所組成的,而每個功能模塊所須要的數據對應到數據庫中就是一個或者多個表。而在架構設計中,各個功能模塊相互之間的交互點越統一越少,系統的耦合度就越低,系統各個模塊的維護性以及擴展性也就越好。這樣的系統,實現數據的垂直切分也就越容易。
當咱們的功能模塊越清晰,耦合度越低,數據垂直切分的規則定義也就越容易。徹底能夠根據功能模塊來進行數據的切分,不一樣功能模塊的數據存放於不一樣的數據庫主機中,能夠很容易就避免掉跨數據庫的 Join 存在,同時系統架構也很是的清晰。

固然,很難有系統可以作到全部功能模塊所使用的表徹底獨立,徹底不須要訪問對方的表或者須要兩個模塊的表進行 Join 操做。這種狀況下,咱們就必須根據實際的應用場景進行評估權衡。決定是遷就應用程序將須要 Join 的表的相關某快都存放在同一個數據庫中, 仍是讓應用程序作更多的事情,也就是程序徹底經過模塊接口取得不一樣數據庫中的數據,而後在程序中完成 Join 操做。
通常來講,若是是一個負載相對不是很大的系統,並且表關聯又很是的頻繁,那可能數據庫讓步,將幾個相關模塊合併在一塊兒減小應用程序的工做的方案能夠減小較多的工做量,是一個可行的方案。
固然,經過數據庫的讓步,讓多個模塊集中共用數據源,實際上也是簡介的默許了各模塊架構耦合度增大的發展,可能會讓之後的架構愈來愈惡化。尤爲是當發展到必定階段以後, 發現數據庫實在沒法承擔這些表所帶來的壓力,不得不面臨再次切分的時候,所帶來的架構改形成本可能會遠遠大於最初的時候。
因此,在數據庫進行垂直切分的時候,如何切分,切分到什麼樣的程度,是一個比較考驗人的難題。只能在實際的應用場景中經過平衡各方面的成本和收益,才能分析出一個真正適合本身的拆分方案。
好比在本書所使用示例系統的 example 數據庫,咱們簡單的分析一下,而後再設計一個簡單的切分規則,進行一次垂直垂直拆分。
系統功能能夠基本分爲四個功能模塊:用戶,羣組消息,相冊以及事件,分別對應爲以下這些表:

1. 用戶模塊表:user,user_profile,user_group,user_photo_album
2. 羣組討論表:groups,group_message,group_message_content,top_message
3. 相冊相關表:photo,photo_album,photo_album_relation,photo_comment
4. 事件信息表:event 架構

初略一看,沒有哪個模塊能夠脫離其餘模塊獨立存在,模塊與模塊之間都存在着關係,莫非沒法切分?
固然不是,咱們再稍微深刻分析一下,能夠發現,雖然各個模塊所使用的表之間都有關聯,可是關聯關係還算比較清晰,也比較簡單。
◆ 羣組討論模塊和用戶模塊之間主要存在經過用戶或者是羣組關係來進行關聯。通常關聯的時候都會是經過用戶的 id 或者 nick_name 以及 group 的 id 來進行關聯,經過模塊之間的接口實現不會帶來太多麻煩;

◆ 相冊模塊僅僅與用戶模塊存在經過用戶的關聯。這兩個模塊之間的關聯基本就有經過用戶 id 關聯的內容,簡單清晰,接口明確;

◆ 事件模塊與各個模塊可能都有關聯,可是都只關注其各個模塊中對象的ID信息, 一樣能夠作到很容易分拆。

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

以下圖所示: 負載均衡

經過這樣的垂直切分以後,以前只能經過一個數據庫來提供的服務,就被分拆成四個數據庫來提供服務,服務能力天然是增長几倍了。
垂直切分的優勢
◆ 數據庫的拆分簡單明瞭,拆分規則明確;
◆ 應用程序模塊清晰明確,整合容易;
◆ 數據維護方便易行,容易定位;
垂直切分的缺點
◆ 部分表關聯沒法在數據庫級別完成,須要在程序中完成;
◆ 對於訪問極其頻繁且數據量超大的表仍然存在性能平靜,不必定能知足要求;
◆ 事務處理相對更爲複雜;
◆ 切分達到必定程度以後,擴展性會遇到限制;
◆ 過分切分可能會帶來系統過分複雜而難以維護。
針對於垂直切分可能遇到數據切分及事務問題,在數據庫層面實在是很難找到一個較好的處理方案。實際應用案例中,數據庫的垂直切分大可能是與應用系統的模塊相對應,同一個模塊的數據源存放於同一個數據庫中,能夠解決模塊內部的數據關聯問題。而模塊與模塊之間,則經過應用程序以服務接口方式來相互提供所須要的數據。雖然這樣作在數據庫的整體操做次數方面確實會有所增長,可是在系統總體擴展性以及架構模塊化方面,都是有益的。

可能在某些操做的單次響應時間會稍有增長,可是系統的總體性能極可能反而會有必定的提高。而擴展瓶頸問題,就只能依靠下一節將要介紹的數據水平切分架構來解決了。

14.3 數據的水平切分框架

上面一節分析介紹了數據的垂直切分,這一節再分析一下數據的水平切分。數據的垂直切分基本上能夠簡單的理解爲按照表按照模塊來切分數據,而水平切分就再也不是按照表或者是功能模塊來切分了。通常來講,簡單的水平切分主要是將某個訪問極其平凡的表再按照某個字段的某種規則來分散到多個表之中,每一個表中包含一部分數據。
簡單來講,咱們能夠將數據的水平切分理解爲是按照數據行的切分,就是將表中的某些行切分到一個數據庫,而另外的某些行又切分到其餘的數據庫中。固然,爲了可以比較容易的斷定各行數據被切分到哪一個數據庫中了,切分老是都須要按照某種特定的規則來進行的。

如根據某個數字類型字段基於特定數目取模,某個時間類型字段的範圍,或者是某個字符類型字段的 hash 值。若是整個系統中大部分核心表均可以經過某個字段來進行關聯,那這個字段天然是一個進行水平分區的上上之選了,固然,很是特殊沒法使用就只能另選其餘了。
通常來講,像如今互聯網很是火爆的 Web2.0 類型的網站,基本上大部分數據都可以經過會員用戶信息關聯上,可能不少核心表都很是適合經過會員 ID 來進行數據的水平切分。 而像論壇社區討論系統,就更容易切分了,很是容易按照論壇編號來進行數據的水平切分。

切分以後基本上不會出現各個庫之間的交互。
如咱們的示例系統,全部數據都是和用戶關聯的,那麼咱們就能夠根據用戶來進行水平拆分,將不一樣用戶的數據切分到不一樣的數據庫中。固然,惟一有點區別的是用戶模塊中的groups 表和用戶沒有直接關係,因此 groups 不能根據用戶來進行水平拆分。對於這種特殊狀況下的表,咱們徹底能夠獨立出來,單獨放在一個獨立的數據庫中。其實這個作法能夠說是利用了前面一節所介紹的「數據的垂直切分」方法,我將在下一節中更爲詳細的介紹這種垂直切分與水平切分同時使用的聯合切分方法。
因此,對於咱們的示例數據庫來講,大部分的表均可以根據用戶 ID 來進行水平的切分。不一樣用戶相關的數據進行切分以後存放在不一樣的數據庫中。如將全部用戶 ID 經過 2 取模而後分別存放於兩個不一樣的數據庫中。每一個和用戶 ID 關聯上的表均可以這樣切分。這樣,基本上每一個用戶相關的數據,都在同一個數據庫中,即便是須要關聯,也能夠很是簡單的關聯上。
咱們能夠經過下圖來更爲直觀的展現水平切分相關信息:

水平切分的優勢
◆ 表關聯基本可以在數據庫端所有完成;
◆ 不會存在某些超大型數據量和高負載的表遇到瓶頸的問題;
◆ 應用程序端總體架構改動相對較少;
◆ 事務處理相對簡單;
◆ 只要切分規則可以定義好,基本上較難遇到擴展性限制;
水平切分的缺點
◆ 切分規則相對更爲複雜,很難抽象出一個可以知足整個數據庫的切分規則;
◆ 後期數據的維護難度有所增長,人爲手工定位數據更困難;
◆ 應用系統各模塊耦合度較高,可能會對後面數據的遷移拆分形成必定的困難。

14.4 垂直與水平聯合切分的使用
上面兩節內容中,咱們分別,瞭解了「垂直」和「水平」這兩種切分方式的實現以及切分以後的架構信息,同時也分析了兩種架構各自的優缺點。可是在實際的應用場景中,除了那些負載並非太大,業務邏輯也相對較簡單的系統能夠經過上面兩種切分方法之一來解決擴展性問題以外,恐怕其餘大部分業務邏輯稍微複雜一點,系統負載大一些的系統,都沒法經過上面任何一種數據的切分方法來實現較好的擴展性,而須要將上述兩種切分方法結合使用,不一樣的場景使用不一樣的切分方法。

在這一節中,我將結合垂直切分和水平切分各自的優缺點,進一步完善咱們的總體架構, 讓系統的擴展性進一步提升。
通常來講,咱們數據庫中的全部表很難經過某一個(或少數幾個)字段所有關聯起來, 因此很難簡單的僅僅經過數據的水平切分來解決全部問題。而垂直切分也只能解決部分問題,對於那些負載很是高的系統,即便僅僅只是單個表都沒法經過單臺數據庫主機來承擔其負載。咱們必須結合「垂直」和「水平」兩種切分方式同時使用,充分利用二者的優勢,避開其缺點。
每個應用系統的負載都是一步一步增加上來的,在開始遇到性能瓶頸的時候,大多數架構師和DBA都會選擇先進行數據的垂直拆分,由於這樣的成本最早,最符合這個時期所追求的最大投入產出比。然而,隨着業務的不斷擴張,系統負載的持續增加,在系統穩定一段時期以後,通過了垂直拆分以後的數據庫集羣可能又再一次不堪重負,遇到了性能瓶頸。
這時候咱們該如何抉擇?是再次進一步細分模塊呢,仍是尋求其餘的辦法來解決?若是咱們再一次像最開始那樣繼續細分模塊,進行數據的垂直切分,那咱們可能在不久的未來,又會遇到如今所面對的一樣的問題。並且隨着模塊的不斷的細化,應用系統的架構也會愈來愈複雜,整個系統極可能會出現失控的局面。
這時候咱們就必需要經過數據的水平切分的優點,來解決這裏所遇到的問題。並且,咱們徹底沒必要要在使用數據水平切分的時候,推倒以前進行數據垂直切分的成果,而是在其基礎上利用水平切分的優點來避開垂直切分的弊端,解決系統複雜性不斷擴大的問題。而水平拆分的弊端(規則難以統一)也已經被以前的垂直切分解決掉了,讓水平拆分能夠進行的駕輕就熟。
對於咱們的示例數據庫,假設在最開始,咱們進行了數據的垂直切分,然而隨着業務的不斷增加,數據庫系統遇到了瓶頸,咱們選擇重構數據庫集羣的架構。如何重構?考慮到以前已經作好了數據的垂直切分,並且模塊結構清晰明確。而業務增加的勢頭愈來愈猛,即便如今進一步再次拆分模塊,也堅持不了過久。咱們選擇了在垂直切分的基礎上再進行水平拆分。
在經歷過垂直拆分後的各個數據庫集羣中的每個都只有一個功能模塊,而每一個功能模塊中的全部表基本上都會與某個字段進行關聯。如用戶模塊所有均可以經過用戶 ID 進行切分,羣組討論模塊則都經過羣組 ID 來切分,相冊模塊則根據相冊 ID 來進切分,最後的事件通知信息表考慮到數據的時限性(僅僅只會訪問最近某個事件段的信息),則考慮按時間來切分。
下圖展現了切分後的整個架構:

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

聯合切分的優勢
◆ 能夠充分利用垂直切分和水平切分各自的優點而避免各自的缺陷;
◆ 讓系統擴展性獲得最大化提高; 分佈式

聯合切分的缺點
◆ 數據庫系統架構比較複雜,維護難度更大;
◆ 應用程序架構也相對更復雜;

14.5 數據切分及整合方案模塊化

經過前面的章節,咱們已經很清楚了經過數據庫的數據切分能夠極大的提升系統的擴展性。可是,數據庫中的數據在通過垂直和(或)水平切分被存放在不一樣的數據庫主機以後,應用系統面臨的最大問題就是如何來讓這些數據源獲得較好的整合,可能這也是不少讀者朋友很是關心的一個問題。這一節咱們主要針對的內容就是分析可使用的各類能夠幫助咱們實現數據切分以及數據整合的總體解決方案。
數據的整合很難依靠數據庫自己來達到這個效果,雖然 MySQL 存在 Federated 存儲引擎,能夠解決部分相似的問題,可是在實際應用場景中卻很難較好的運用。那咱們該如何來整合這些分散在各個 MySQL 主機上面的數據源呢?
總的來講,存在兩種解決思路:

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

可能90%以上的人在面對上面這兩種解決思路的時候都會傾向於選擇第二種,尤爲是系統不斷變得龐大複雜的時候。確實,這是一個很是正確的選擇,雖然短時間內須要付出的成本可能會相對更大一些,可是對整個系統的擴展性來講,是很是有幫助的。
因此,對於第一種解決思路我這裏就不許備過多的分析,下面我重點分析一下在第二種解決思路中的一些解決方案。

★ 自行開發中間代理層
在決定選擇經過數據庫的中間代理層來解決數據源整合的架構方向以後,有很多公司(或者企業)選擇了經過自行開發符合自身應用特定場景的代理層應用程序。
經過自行開發中間代理層能夠最大程度的應對自身應用的特定,最大化的定製不少個性化需求,在面對變化的時候也能夠靈活的應對。這應該說是自行開發代理層最大的優點了。

固然,選擇自行開發,享受讓個性化定製最大化的樂趣的同時,天然也須要投入更多的成原本進行前期研發以及後期的持續升級改進工做,並且自己的技術門檻可能也比簡單的Web 應用要更高一些。因此,在決定選擇自行開發以前,仍是須要進行比較全面的評估爲好。
因爲自行開發更多時候考慮的是如何更好的適應自身應用系統,應對自身的業務場景, 因此這裏也很差分析太多。後面咱們主要分析一下當前比較流行的幾種數據源整合解決方案。

★ 利用 MySQL Proxy 實現數據切分及整合
MySQL Proxy 是 MySQL 官方提供的一個數據庫代理層產品,和 MySQL Server 同樣,一樣是一個基於 GPL 開源協議的開源產品。可用來監視、分析或者傳輸他們之間的通信信息。他的靈活性容許你最大限度的使用它,目前具有的功能主要有鏈接路由,Query 分析,Query 過濾和修改,負載均衡,以及基本的 HA 機制等。
實際上,MySQL Proxy 自己並不具備上述全部的這些功能,而是提供了實現上述功能的基礎。要實現這些功能,還須要經過咱們自行編寫 LUA 腳原本實現。
MySQL Proxy 其實是在客戶端請求與 MySQL Server 之間創建了一個鏈接池。全部客戶端請求都是發向 MySQL Proxy,而後經由 MySQL Proxy進行相應的分析,判斷出是讀操做仍是寫操做,分發至對應的 MySQL Server 上。對於多節點 Slave 集羣,也能夠起作到負載均衡的效果。如下是 MySQL Proxy 的基本架構圖:

經過上面的架構簡圖,咱們能夠很清晰的看出 MySQL Proxy 在實際應用中所處的位置, 以及能作的基本事情。關於 MySQL Proxy 更爲詳細的實施細則在 MySQL 官方文檔中有很是詳細的介紹和示例,感興趣的讀者朋友能夠直接從 MySQL 官方網站免費下載或者在線閱讀,我這裏就不累述浪費紙張了。

★ 利用 Amoeba 實現數據切分及整合
Amoeba 是一個基於 Java 開發的,專一於解決分佈式數據庫數據源整合 Proxy 程序的開源框架,基於 GPL3 開源協議。目前,Amoeba 已經具備 Query 路由,Query 過濾,讀寫分離,負載均衡以及 HA 機制等相關內容。

Amoeba 主要解決的如下幾個問題:
1. 數據切分後複雜數據源整合;
2. 提供數據切分規則並下降數據切分規則給數據庫帶來的影響;
3. 下降數據庫與客戶端的鏈接數;
4. 讀寫分離路由;
咱們能夠看出,Amoeba 所作的事情,正好就是咱們經過數據切分來提高數據庫的擴展性所須要的。

Amoeba 並非一個代理層的 Proxy 程序,而是一個開發數據庫代理層 Proxy 程序的開發框架,目前基於 Amoeba 所開發的 Proxy 程序有 Amoeba For MySQL 和 Amoeba For Aladin 兩個。
Amoeba For MySQL 主要是專門針對 MySQL 數據庫的解決方案,前端應用程序請求的協議以及後端鏈接的數據源數據庫都必須是 MySQL。對於客戶端的任何應用程序來講,Amoeba For MySQL 和一個 MySQL 數據庫沒有什麼區別,任何使用 MySQL 協議的客戶端請求,均可以被 Amoeba For MySQL 解析並進行相應的處理。下如能夠告訴咱們 Amoeba For MySQL 的架構信息(出自 Amoeba 開發者博客):

Amoeba For Aladin 則是一個適用更爲普遍,功能更爲強大的 Proxy 程序。他能夠同時鏈接不一樣數據庫的數據源爲前端應用程序提供服務,可是僅僅接受符合 MySQL 協議的客戶端應用程序請求。也就是說,只要前端應用程序經過 MySQL 協議鏈接上來以後,Amoeba For Aladin 會自動分析 Query 語句,根據 Query 語句中所請求的數據來自動識別出該所Query 的數據源是在什麼類型數據庫的哪個物理主機上面。下圖展現了 Amoeba For Aladin 的架構細節(出自 Amoeba 開發者博客):

咋一看,二者好像徹底同樣嘛。細看以後,纔會發現二者主要的區別僅在於經過 MySQL Protocal Adapter處理以後,根據分析結果判斷出數據源數據庫,而後選擇特定的 JDBC 驅動和相應協議鏈接後端數據庫。其實經過上面兩個架構圖你們可能也已經發現了 Amoeba 的特色了,他僅僅只是一個開發框架,咱們除了選擇他已經提供的 For MySQL 和 For Aladin 這兩款產品以外,還能夠基於自身的需求進行相應的二次開發,獲得更適應咱們本身應用特色的 Proxy 程序。
當對於使用 MySQL 數據庫來講,不管是 Amoeba For MySQL 仍是 Amoeba For Aladin均可以很好的使用。固然,考慮到任何一個系統越是複雜,其性能確定就會有必定的損失,維護成本天然也會相對更高一些。因此,對於僅僅須要使用 MySQL 數據庫的時候,我仍是建議使用 Amoeba For MySQL。
Amoeba For MySQL 的使用很是簡單,全部的配置文件都是標準的 XML 文件,總共有四個配置文件。分別爲:

◆ amoeba.xml:主配置文件,配置全部數據源以及Amoeba自身的參數設置;
◆ rule.xml:配置全部 Query 路由規則的信息;
◆ functionMap.xml:配置用於解析 Query 中的函數所對應的 Java 實現類;
◆ rullFunctionMap.xml:配置路由規則中須要使用到的特定函數的實現類;
若是您的規則不是太複雜,基本上僅須要使用到上面四個配置文件中的前面兩個就可完成全部工做。Proxy 程序經常使用的功能如讀寫分離,負載均衡等配置都在 amoeba.xml 中進行。
此外,Amoeba 已經支持了實現數據的垂直切分和水平切分的自動路由,路由規則能夠在rule.xml 進行設置。
目前 Amoeba 少有欠缺的主要就是其在線管理功能以及對事務的支持了,曾經在與相關開發者的溝經過程中提出過相關的建議,但願可以提供一個能夠進行在線維護管理的命令行管理工具,方便在線維護使用,獲得的反饋是管理專門的管理模塊已經歸入開發日程了。另外在事務支持方面暫時仍是 Amoeba 沒法作到的,即便客戶端應用在提交給 Amoeba 的請求是包含事務信息的,Amoeba 也會忽略事務相關信息。固然,在通過不斷完善以後,我相信事務支持確定是Amoeba 重點考慮增長的 feature。
關於 Amoeba 更爲詳細的使用方法讀者朋友能夠經過 Amoeba 開發者博客(http://amoeba.sf.net)上面提供的使用手冊獲取,這裏就再也不細述了。

★ 利用 HiveDB 實現數據切分及整合
和前面的 MySQL Proxy 以及 Amoeba 同樣,HiveDB 一樣是一個基於 Java 針對MySQL 數據庫的提供數據切分及整合的開源框架,只是目前的 HiveDB 僅僅支持數據的水平切分。

主要解決大數據量下數據庫的擴展性及數據的高性能訪問問題,同時支持數據的冗餘及基本的 HA 機制。
HiveDB 的實現機制與 MySQL Proxy 和 Amoeba 有必定的差別,他並非藉助 MySQL的 Replication 功能來實現數據的冗餘,而是自行實現了數據冗餘機制,而其底層主要是基於 Hibernate Shards 來實現的數據切分工做。

在 HiveDB 中,經過用戶自定義的各類 Partition keys(其實就是制定數據切分規則), 將數據分散到多個 MySQL Server 中。在訪問的時候,在運行 Query 請求的時候,會自動分析過濾條件,並行從多個 MySQL Server 中讀取數據,併合並結果集返回給客戶端應用程序。
單純從功能方面來說,HiveDB 可能並不如 MySQL Proxy 和 Amoeba 那樣強大,可是其數據切分的思路與前面兩者並沒有本質差別。此外,HiveDB 並不只僅只是一個開源愛好者所共享的內容,而是存在商業公司支持的開源項目。
下面是 HiveDB 官方網站上面一章圖片,描述了 HiveDB 如何來組織數據的基本信息, 雖然不能詳細的表現出太多架構方面的信息,可是也基本能夠展現出其在數據切分方面獨特的一面了。

★ 其餘實現數據切分及整合的解決方案
除了上面介紹的幾個數據切分及整合的總體解決方案以外,還存在不少其餘一樣提供了數據切分與整合的解決方案。如基於 MySQL Proxy 的基礎上作了進一步擴展的 HSCALE,經過 Rails 構建的 Spock Proxy,以及基於 Pathon 的 Pyshards 等等。

無論你們選擇使用哪種解決方案,整體設計思路基本上都不該該會有任何變化,那就是經過數據的垂直和水平切分,加強數據庫的總體服務能力,讓應用系統的總體擴展能力盡量的提高,擴展方式儘量的便捷。
只要咱們經過中間層 Proxy 應用程序較好的解決了數據切分和數據源整合問題,那麼數據庫的線性擴展能力將很容易作到像咱們的應用程序同樣方便,只須要經過添加廉價的PC Server 服務器,便可線性增長數據庫集羣的總體服務能力,讓數據庫再也不輕易成爲應用系統的性能瓶頸。

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

這裏,你們應該對數據切分與整合的實施有了必定的認識了,或許不少讀者朋友都已經根據各類解決方案各自特性的優劣基本選定了適合於本身應用場景的方案,後面的工做主要就是實施準備了。
在實施數據切分方案以前,有些可能存在的問題咱們仍是須要作一些分析的。通常來講,咱們可能遇到的問題主要會有如下幾點:
◆ 引入分佈式事務的問題;
◆ 跨節點 Join 的問題;
◆ 跨節點合併排序分頁問題;

1. 引入分佈式事務的問題
一旦數據進行切分被分別存放在多個 MySQL Server 中以後,無論咱們的切分規則設計的多麼的完美(實際上並不存在完美的切分規則),均可能形成以前的某些事務所涉及到的數據已經不在同一個 MySQL Server 中了。
在這樣的場景下,若是咱們的應用程序仍然按照老的解決方案,那麼勢必須要引入分佈式事務來解決。而在 MySQL 各個版本中,只有從 MySQL 5.0 開始之後的各個版本纔開始對分佈式事務提供支持,並且目前僅有 Innodb 提供分佈式事務支持。不只如此,即便咱們恰好使用了支持分佈式事務的 MySQL 版本,同時也是使用的 Innodb 存儲引擎,分佈式事務自己對於系統資源的消耗就是很大的,性能自己也並非過高。並且引入分佈式事務自己在異常處理方面就會帶來較多比較難控制的因素。
怎麼辦?其實咱們能夠能夠經過一個變通的方法來解決這種問題,首先須要考慮的一件事情就是:是否數據庫是惟一一個可以解決事務的地方呢?其實並非這樣的,咱們徹底能夠結合數據庫以及應用程序二者來共同解決。各個數據庫解決本身身上的事務,而後經過應用程序來控制多個數據庫上面的事務。
也就是說,只要咱們願意,徹底能夠將一個跨多個數據庫的分佈式事務分拆成多個僅處於單個數據庫上面的小事務,並經過應用程序來總控各個小事務。固然,這樣做的要求就是咱們的俄應用程序必需要有足夠的健壯性,固然也會給應用程序帶來一些技術難度。

2. 跨節點 Join 的問題
上面介紹了可能引入分佈式事務的問題,如今咱們再看看須要跨節點 Join 的問題。數據切分以後,可能會形成有些老的 Join 語句沒法繼續使用,由於 Join 使用的數據源可能被切分到多個 MySQL Server 中了。
怎麼辦?這個問題從 MySQL 數據庫角度來看,若是非得在數據庫端來直接解決的話,恐怕只能經過 MySQL 一種特殊的存儲引擎 Federated 來解決了。Federated 存儲引擎是MySQL 解決相似於 Oracle 的 DB Link 之類問題的解決方案。和 Oracle DB Link 的主要區別在於 Federated 會保存一份遠端表結構的定義信息在本地。咋一看,Federated 確實是解決跨節點 Join 很是好的解決方案。可是咱們還應該清楚一點,那就彷佛若是遠端的表結構發生了變動,本地的表定義信息是不會跟着發生相應變化的。若是在更新遠端表結構的時候並無更新本地的 Federated 表定義信息,就極可能形成 Query 運行出錯,沒法獲得正確的結果。
對待這類問題,我仍是推薦經過應用程序來進行處理,先在驅動表所在的 MySQL Server中取出相應的驅動結果集,而後根據驅動結果集再到被驅動表所在的 MySQL Server 中取出相應的數據。可能不少讀者朋友會認爲這樣作對性能會產生必定的影響,是的,確實是會對性能有必定的負面影響,可是除了此法,基本上沒有太多其餘更好的解決辦法了。並且,因爲數據庫經過較好的擴展以後,每臺 MySQL Server 的負載就能夠獲得較好的控制,單純針對單條 Query 來講,其響應時間可能比不切分以前要提升一些,因此性能方面所帶來的負面影響也並非太大。更況且,相似於這種須要跨節點 Join 的需求也並非太多,相對於整體性能而言,可能也只是很小一部分而已。因此爲了總體性能的考慮,偶爾犧牲那麼一點點,實際上是值得的,畢竟系統優化自己就是存在不少取捨和平衡的過程。

3. 跨節點合併排序分頁問題
一旦進行了數據的水平切分以後,可能就並不只僅只有跨節點 Join 沒法正常運行,有些排序分頁的 Query 語句的數據源可能也會被切分到多個節點,這樣形成的直接後果就是這些排序分頁 Query 沒法繼續正常運行。其實這和跨節點 Join 是一個道理,數據源存在於多個節點上,要經過一個 Query 來解決,就和跨節點 Join 是同樣的操做。一樣Federated 也能夠部分解決,固然存在的風險也同樣。 仍是一樣的問題,怎麼辦?我一樣仍然繼續建議經過應用程序來解決。
如何解決?解決的思路大致上和跨節點 Join 的解決相似,可是有一點和跨節點 Join 不太同樣,Join 不少時候都有一個驅動與被驅動的關係,因此 Join 自己涉及到的多個表之間的數據讀取通常都會存在一個順序關係。可是排序分頁就不太同樣了,排序分頁的數據源基本上能夠說是一個表(或者一個結果集),自己並不存在一個順序關係,因此在從多個數據源取數據的過程是徹底能夠並行的。這樣,排序分頁數據的取數效率咱們能夠作的比跨庫 Join 更高,因此帶來的性能損失相對的要更小,在有些狀況下可能比在原來未進行數據切分的數據庫中效率更高了。固然,不管是跨節點 Join 仍是跨節點排序分頁,都會使咱們的應用服務器消耗更多的資源,尤爲是內存資源,由於咱們在讀取訪問以及合併結果集的這個過程須要比原來處理更多的數據。
分析到這裏,可能不少讀者朋友會發現,上面全部的這些問題,我給出的建議基本上都是經過應用程序來解決。你們可能內心開始犯嘀咕了,是否是由於我是 DBA,因此就不少事情都扔給應用架構師和開發人員了? 其實徹底不是這樣,首先應用程序因爲其特殊性,能夠很是容易作到很好的擴展性,可是數據庫就不同,必須藉助不少其餘的方式才能作到擴展,並且在這個擴展過程當中,很難避免帶來有些原來在集中式數據庫中能夠解決但被切分開成一個數據庫集羣以後就成爲一個難題的狀況。要想讓系統總體獲得最大限度的擴展,咱們只能讓應用程序作更多的事情,來解決數據庫集羣沒法較好解決的問題。

14.7 小結

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

 

摘自:《MySQL性能調優與架構設計》簡朝陽

轉載請註明出處:

做者:JesseLZJ
出處:http://jesselzj.cnblogs.com

相關文章
相關標籤/搜索