天天看點

可擴充性設計之資料切分

通過mysqlreplication功能所實作的擴充總是會受到資料庫大小的限制,一旦資料庫過于龐大,尤其是當寫入過于頻繁,很難由一台主機支撐的時候,我們還是會面臨到擴充瓶頸。這時候,我們就必須許找其他技術手段來解決這個瓶頸,那就是我們這一章所要介紹惡的資料切分技術。

可能很多讀者朋友在網上或者雜志上面都已經多次見到關于資料切分的相關文章了,隻不過在有些文章中稱之為資料的sharding。其實不管是稱之為資料的sharding還是資料的切分,其概念都是一樣的。簡單來說,就是指通過某種特定的條件,将我們存放在同一個資料庫中的資料分散存放到多個資料庫(主機)上面,以達到分散單台裝置負載的效果。資料的切分同時還可以提高系統的總體可用性,因為單台裝置crash之後,隻有總體資料的某部分不可用,而不是所有的資料。

資料的切分(sharding)根據其切分規則的類型,可以分為兩種切分模式。一種是按照不同的表(或者schema)來切分到不同的資料庫(主機)之上,這種切可以稱之為資料的垂直(縱向)切分;另外一種則是根據表中的資料的邏輯關系,将同一個表中的資料按照某種條件拆分到多台資料庫(主機)上面,這種切分稱之為資料的水準(橫向)切分。

垂直切分的最大特點就是規則簡單,實施也更為友善,尤其适合各業務之間的耦合度非常低,互相影響很小,業務邏輯非常清晰的系統。在這種系統中,可以很容易做到将不同業務子產品所使用的表分拆到不同的資料庫中。根據不同的表來進行拆分,對應用程式的影響也更小,拆分規則也會比較簡單清晰。

水準切分于垂直切分相比,相對來說稍微複雜一些。因為要将同一個表中的不同資料拆分到不同的資料庫中,對于應用程式來說,拆分規則本身就較根據表名來拆分更為複雜,後期的資料維護也會更為複雜一些。

當我們某個(或者某些)表的資料量和通路量特别的大,通過垂直切分将其放在獨立的裝置上後仍然無法滿足性能要求,這時候我們就必須将垂直切分和水準切分相結合,先垂直切分,然後再水準切分,才能解決這種超大型表的性能問題。

下面我們就針對垂直、水準以及組合切分這三種資料切分方式的架構實作及切分後資料的整合進行相應的分析。

我們先來看一下,資料的垂直切分到底是如何一個切分法的。資料的垂直切分,也可以稱之為縱向切分。将資料庫想象成為由很多個一大塊一大塊的“資料塊”(表)組成,我們垂直的将這些“資料塊”切開,然後将他們分散到多台資料庫主機上面。這樣的切分方法就是一個垂直(縱向)的資料切分。

一個架構設計較好的應用系統,其總體功能肯定是由很多個功能子產品所組成的,而每一個功能子產品所需要的資料對應到資料庫中就是一個或者多個表。而在架構設計中,各個功能子產品互相之間的互動點越統一越少,系統的耦合度就越低,系統各個子產品的維護性以及擴充性也就越好。這樣的系統,實作資料的垂直切分也就越容易。

當我們的功能子產品越清晰,耦合度越低,資料垂直切分的規則定義也就越容易。完全可以根據功能子產品來進行資料的切分,不同功能子產品的資料存放于不同的資料庫主機中,可以很容易就避免掉跨資料庫的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資訊,同樣可以做到很容易分拆。

是以,我們第一步可以将資料庫按照功能子產品相關的表進行一次垂直拆分,每個子產品所涉及的表單獨到一個資料庫中,子產品與子產品之間的表關聯都在應用系統端通過藉口來處理。如下圖所示:

可擴充性設計之資料切分

通過這樣的垂直切分之後,之前隻能通過一個資料庫來提供的服務,就被分拆成四個資料庫來提供服務,服務能力自然是增加幾倍了。

垂直切分的優點

◆ 資料庫的拆分簡單明了,拆分規則明确;

◆ 應用程式子產品清晰明确,整合容易;

◆ 資料維護友善易行,容易定位;

垂直切分的缺點

◆ 部分表關聯無法在資料庫級别完成,需要在程式中完成;

◆ 對于通路極其頻繁且資料量超大的表仍然存在性能平靜,不一定能滿足要求;

◆ 事務處理相對更為複雜;

◆ 切分達到一定程度之後,擴充性會遇到限制;

◆ 過讀切分可能會帶來系統過渡複雜而難以維護。

針對于垂直切分可能遇到資料切分及事務問題,在資料庫層面實在是很難找到一個較好的處理方案。實際應用案例中,資料庫的垂直切分大多是與應用系統的子產品相對應,同一個子產品的資料源存放于同一個資料庫中,可以解決子產品内部的資料關聯問題。而子產品與子產品之間,則通過應用程式以服務接口方式來互相提供所需要的資料。雖然這樣做在資料庫的總體操作次數方面确實會有所增加,但是在系統整體擴充性以及架構子產品化方面,都是有益的。可能在某些操作的單次響應時間會稍有增加,但是系統的整體性能很可能反而會有一定的提升。而擴充瓶頸問題,就隻能依靠下一節将要介紹的資料水準切分架構來解決了。

上面一節分析介紹了資料的垂直切分,這一節再分析一下資料的水準切分。資料的垂直切分基本上可以簡單的了解為按照表按照子產品來切分資料,而水準切分就不再是按照表或者是功能子產品來切分了。一般來說,簡單的水準切分主要是将某個通路極其平凡的表再按照某個字段的某種規則來分散到多個表之中,每個表中包含一部分資料。

簡單來說,我們可以将資料的水準切分了解為是按照資料行的切分,就是将表中的某些行切分到一個資料庫,而另外的某些行又切分到其他的資料庫中。當然,為了能夠比較容易的判定各行資料被切分到哪個資料庫中了,切分總是都需要按照某種特定的規則來進行的。如根據某個數字類型字段基于特定數目取模,某個時間類型字段的範圍,或者是某個字元類型字段的hash值。如果整個系統中大部分核心表都可以通過某個字段來進行關聯,那這個字段自然是一個進行水準分區的上上之選了,當然,非常特殊無法使用就隻能另選其他了。

一般來說,像現在網際網路非常火爆的web2.0類型的網站,基本上大部分資料都能夠通過會員使用者資訊關聯上,可能很多核心表都非常适合通過會員id來進行資料的水準切分。而像論壇社群讨論系統,就更容易切分了,非常容易按照論壇編号來進行資料的水準切分。切分之後基本上不會出現各個庫之間的互動。

如我們的示例系統,所有資料都是和使用者關聯的,那麼我們就可以根據使用者來進行水準拆分,将不同使用者的資料切分到不同的資料庫中。當然,唯一有點差別的是使用者子產品中的groups表和使用者沒有直接關系,是以groups不能根據使用者來進行水準拆分。對于這種特殊情況下的表,我們完全可以獨立出來,單獨放在一個獨立的資料庫中。其實這個做法可以說是利用了前面一節所介紹的“資料的垂直切分”方法,我将在下一節中更為詳細的介紹這種垂直切分與水準切分同時使用的聯合切分方法。

是以,對于我們的示例資料庫來說,大部分的表都可以根據使用者id來進行水準的切分。不同使用者相關的資料進行切分之後存放在不同的資料庫中。如将所有使用者id通過2取模然後分别存放于兩個不同的資料庫中。每個和使用者id關聯上的表都可以這樣切分。這樣,基本上每個使用者相關的資料,都在同一個資料庫中,即使是需要關聯,也可以非常簡單的關聯上。

我們可以通過下圖來更為直覺的展示水準切分相關資訊:水準切分的優點

可擴充性設計之資料切分

◆ 表關聯基本能夠在資料庫端全部完成;

◆ 不會存在某些超大型資料量和高負載的表遇到瓶頸的問題;

◆ 應用程式端整體架構改動相對較少;

◆ 事務處理相對簡單;

◆ 隻要切分規則能夠定義好,基本上較難遇到擴充性限制;

水準切分的缺點

◆ 切分規則相對更為複雜,很難抽象出一個能夠滿足整個資料庫的切分規則;

◆ 後期資料的維護難度有所增加,人為手工定位資料更困難;

◆ 應用系統各子產品耦合度較高,可能會對後面資料的遷移拆分造成一定的困難。

上面兩節内容中,我們分别,了解了“垂直”和“水準”這兩種切分方式的實作以及切分之後的架構資訊,同時也分析了兩種架構各自的優缺點。但是在實際的應用場景中,除了那些負載并不是太大,業務邏輯也相對較簡單的系統可以通過上面兩種切分方法之一來解決擴充性問題之外,恐怕其他大部分業務邏輯稍微複雜一點,系統負載大一些的系統,都無法通過上面任何一種資料的切分方法來實作較好的擴充性,而需要将上述兩種切分方法結合使用,不同的場景使用不同的切分方法。

在這一節中,我将結合垂直切分和水準切分各自的優缺點,進一步完善我們的整體架構,讓系統的擴充性進一步提高。

一般來說,我們資料庫中的所有表很難通過某一個(或少數幾個)字段全部關聯起來,是以很難簡單的僅僅通過資料的水準切分來解決所有問題。而垂直切分也隻能解決部分問題,對于那些負載非常高的系統,即使僅僅隻是單個表都無法通過單台資料庫主機來承擔其負載。我們必須結合“垂直”和“水準”兩種切分方式同時使用,充分利用兩者的優點,避開其缺點。

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

這時候我們該如何抉擇?是再次進一步細分子產品呢,還是尋求其他的辦法來解決?如果我們再一次像最開始那樣繼續細分子產品,進行資料的垂直切分,那我們可能在不久的将來,又會遇到現在所面對的同樣的問題。而且随着子產品的不斷的細化,應用系統的架構也會越來越複雜,整個系統很可能會出現失控的局面。

這時候我們就必須要通過資料的水準切分的優勢,來解決這裡所遇到的問題。而且,我們完全不必要在使用資料水準切分的時候,推倒之前進行資料垂直切分的成果,而是在其基礎上利用水準切分的優勢來避開垂直切分的弊端,解決系統複雜性不斷擴大的問題。而水準拆分的弊端(規則難以統一)也已經被之前的垂直切分解決掉了,讓水準拆分可以進行的得心應手。

對于我們的示例資料庫,假設在最開始,我們進行了資料的垂直切分,然而随着業務的不斷增長,資料庫系統遇到了瓶頸,我們選擇重構資料庫叢集的架構。如何重構?考慮到之前已經做好了資料的垂直切分,而且子產品結構清晰明确。而業務增長的勢頭越來越猛,即使現在進一步再次拆分子產品,也堅持不了太久。我們選擇了在垂直切分的基礎上再進行水準拆分。

在經曆過垂直拆分後的各個資料庫叢集中的每一個都隻有一個功能子產品,而每個功能子產品中的所有表基本上都會與某個字段進行關聯。如使用者子產品全部都可以通過使用者id進行切分,群組讨論子產品則都通過群組id來切分,相冊子產品則根據相冊id來進切分,最後的事件通知資訊表考慮到資料的時限性(僅僅隻會通路最近某個事件段的資訊),則考慮按時間來切分。

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

可擴充性設計之資料切分

實際上,在很多大型的應用系統中,垂直切分和水準切這兩種資料的切分方法基本上都是并存的,而且經常在不斷的交替進行,以不斷的增加系統的擴充能力。我們在應對不同的應用場景的時候,也需要充分考慮到這兩種切分方法各自的局限,以及各自的優勢,在不同的時期(負載壓力)使用不同的結合方式。

聯合切分的優點

◆ 可以充分利用垂直切分和水準切分各自的優勢而避免各自的缺陷;

◆ 讓系統擴充性得到最大化提升;

聯合切分的缺點

◆ 資料庫系統架構比較複雜,維護難度更大;

◆ 應用程式架構也相對更複雜;

通過前面的章節,我們已經很清楚了通過資料庫的資料切分可以極大的提高系統的擴充性。但是,資料庫中的資料在經過垂直和(或)水準切分被存放在不同的資料庫主機之後,應用系統面臨的最大問題就是如何來讓這些資料源得到較好的整合,可能這也是很多讀者朋友非常關心的一個問題。這一節我們主要針對的内容就是分析可以使用的各種可以幫助我們實作資料切分以及資料整合的整體解決方案。

資料的整合很難依靠資料庫本身來達到這個效果,雖然mysql存在federated存儲引擎,可以解決部分類似的問題,但是在實際應用場景中卻很難較好的運用。那我們該如何來整合這些分散在各個mysql主機上面的資料源呢?

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

1. 在每個應用程式子產品中配置管理自己需要的一個(或者多個)資料源,直接通路各個資料庫,在子產品内完成資料的整合;

2. 通過中間代理層來統一管理所有的資料源,後端資料庫叢集對前端應用程式透明;

可能90%以上的人在面對上面這兩種解決思路的時候都會傾向于選擇第二種,尤其是系統不斷變得龐大複雜的時候。确實,這是一個非常正确的選擇,雖然短期内需要付出的成本可能會相對更大一些,但是對整個系統的擴充性來說,是非常有幫助的。

是以,對于第一種解決思路我這裡就不準備過多的分析,下面我重點分析一下在第二種解決思路中的一些解決方案。

自行開發中間代理層

在決定選擇通過資料庫的中間代理層來解決資料源整合的架構方向之後,有不少公司(或者企業)選擇了通過自行開發符合自身應用特定場景的代理層應用程式。

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

當然,選擇自行開發,享受讓個性化定制最大化的樂趣的同時,自然也需要投入更多的成本來進行前期研發以及後期的持續更新改進工作,而且本身的技術門檻可能也比簡單的web應用要更高一些。是以,在決定選擇自行開發之前,還是需要進行比較全面的評估為好。

由于自行開發更多時候考慮的是如何更好的适應自身應用系統,應對自身的業務場景,是以這裡也不好分析太多。後面我們主要分析一下目前比較流行的幾種資料源整合解決方案。

利用mysqlproxy實作資料切分及整合

mysqlproxy是mysql官方提供的一個資料庫代理層産品,和mysqlserver一樣,同樣是一個基于gpl開源協定的開源産品。可用來監視、分析或者傳輸他們之間的通訊資訊。他的靈活性允許你最大限度的使用它,目前具備的功能主要有連接配接路由,query分析,query過濾和修改,負載均衡,以及基本的ha機制等。

實際上,mysqlproxy本身并不具有上述所有的這些功能,而是提供了實作上述功能的基礎。要實作這些功能,還需要通過我們自行編寫lua腳本來實作。

mysqlproxy實際上是在用戶端請求與mysqlserver之間建立了一個連接配接池。所有用戶端請求都是發向mysqlproxy,然後經由mysqlproxy進行相應的分析,判斷出是讀操作還是寫操作,分發至對應的mysqlserver上。對于多節點slave叢集,也可以起做到負載均衡的效果。以下是mysqlproxy的基本架構圖:

可擴充性設計之資料切分

通過上面的架構簡圖,我們可以很清晰的看出mysqlproxy在實際應用中所處的位置,以及能做的基本事情。關于mysqlproxy更為詳細的實施細則在mysql官方文檔中有非常詳細的介紹和示例,感興趣的讀者朋友可以直接從mysql官方網站免費下載下傳或者線上閱讀,我這裡就不累述浪費紙張了。

利用amoeba實作資料切分及整合

amoeba是一個基于java開發的,專注于解決分布式資料庫資料源整合proxy程式的開源架構,基于gpl3開源協定。目前,amoeba已經具有query路由,query過濾,讀寫分離,負載均衡以及ha機制等相關内容。

amoeba 主要解決的以下幾個問題:

1. 資料切分後複雜資料源整合;

2. 提供資料切分規則并降低資料切分規則給資料庫帶來的影響;

3. 降低資料庫與用戶端的連接配接數;

4. 讀寫分離路由;

我們可以看出,amoeba所做的事情,正好就是我們通過資料切分來提升資料庫的擴充性所需要的。

amoeba并不是一個代理層的proxy程式,而是一個開發資料庫代理層proxy程式的開發架構,目前基于amoeba所開發的proxy程式有amoebaformysql和amoebaforaladin兩個。

amoebaformysql主要是專門針對mysql資料庫的解決方案,前端應用程式請求的協定以及後端連接配接的資料源資料庫都必須是mysql。對于用戶端的任何應用程式來說,amoebaformysql和一個mysql資料庫沒有什麼差別,任何使用mysql協定的用戶端請求,都可以被amoebaformysql解析并進行相應的處理。下如可以告訴我們amoebaformysql的架構資訊(出自amoeba開發者部落格):

可擴充性設計之資料切分

amoebaforaladin則是一個适用更為廣泛,功能更為強大的proxy程式。他可以同時連接配接不同資料庫的資料源為前端應用程式提供服務,但是僅僅接受符合mysql協定的用戶端應用程式請求。也就是說,隻要前端應用程式通過mysql協定連接配接上來之後,amoebaforaladin會自動分析query語句,根據query語句中所請求的資料來自動識别出該所query的資料源是在什麼類型資料庫的哪一個實體主機上面。下圖展示了amoebaforaladin的架構細節(出自amoeba開發者部落格):

可擴充性設計之資料切分

咋一看,兩者好像完全一樣嘛。細看之後,才會發現兩者主要的差別僅在于通過mysqlprotocaladapter處理之後,根據分析結果判斷出資料源資料庫,然後選擇特定的jdbc驅動和相應協定連接配接後端資料庫。

其實通過上面兩個架構圖大家可能也已經發現了amoeba的特點了,他僅僅隻是一個開發架構,我們除了選擇他已經提供的formysql和foraladin這兩款産品之外,還可以基于自身的需求進行相應的二次開發,得到更适應我們自己應用特點的proxy程式。

當對于使用mysql資料庫來說,不論是amoebaformysql還是amoebaforaladin都可以很好的使用。當然,考慮到任何一個系統越是複雜,其性能肯定就會有一定的損失,維護成本自然也會相對更高一些。是以,對于僅僅需要使用mysql資料庫的時候,我還是建議使用amoebaformysql。

amoebaformysql的使用非常簡單,所有的配置檔案都是标準的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實作資料切分及整合

和前面的mysqlproxy以及amoeba一樣,hivedb同樣是一個基于java針對mysql資料庫的提供資料切分及整合的開源架構,隻是目前的hivedb僅僅支援資料的水準切分。主要解決大資料量下資料庫的擴充性及資料的高性能通路問題,同時支援資料的備援及基本的ha機制。

hivedb的實作機制與mysqlproxy和amoeba有一定的差異,他并不是借助mysql的replication功能來實作資料的備援,而是自行實作了資料備援機制,而其底層主要是基于hibernateshards來實作的資料切分工作。

在hivedb中,通過使用者自定義的各種partitionkeys(其實就是制定資料切分規則),将資料分散到多個mysqlserver中。在通路的時候,在運作query請求的時候,會自動分析過濾條件,并行從多個mysqlserver中讀取資料,并合并結果集傳回給用戶端應用程式。

單純從功能方面來講,hivedb可能并不如mysqlproxy和amoeba那樣強大,但是其資料切分的思路與前面二者并無本質差異。此外,hivedb并不僅僅隻是一個開源愛好者所共享的内容,而是存在商業公司支援的開源項目。

下面是hivedb官方網站上面一章圖檔,描述了hivedb如何來組織資料的基本資訊,雖然不能詳細的表現出太多架構方面的資訊,但是也基本可以展示出其在資料切分方面獨特的一面了。

可擴充性設計之資料切分

其他實作資料切分及整合的解決方案

除了上面介紹的幾個資料切分及整合的整體解決方案之外,還存在很多其他同樣提供了資料切分與整合的解決方案。如基于mysqlproxy的基礎上做了進一步擴充的hscale,通過rails建構的spockproxy,以及基于pathon的pyshards等等。

不管大家選擇使用哪一種解決方案,總體設計思路基本上都不應該會有任何變化,那就是通過資料的垂直和水準切分,增強資料庫的整體服務能力,讓應用系統的整體擴充能力盡可能的提升,擴充方式盡可能的便捷。

隻要我們通過中間層proxy應用程式較好的解決了資料切分和資料源整合問題,那麼資料庫的線性擴充能力将很容易做到像我們的應用程式一樣友善,隻需要通過添加廉價的pcserver伺服器,即可線性增加資料庫叢集的整體服務能力,讓資料庫不再輕易成為應用系統的性能瓶頸。

這裡,大家應該對資料切分與整合的實施有了一定的認識了,或許很多讀者朋友都已經根據各種解決方案各自特性的優劣基本標明了适合于自己應用場景的方案,後面的工作主要就是實施準備了。

在實施資料切分方案之前,有些可能存在的問題我們還是需要做一些分析的。一般來說,我們可能遇到的問題主要會有以下幾點:

◆ 引入分布式事務的問題;

◆跨節點join的問題;

◆ 跨節點合并排序分頁問題;

1. 引入分布式事務的問題

一旦資料進行切分被分别存放在多個mysqlserver中之後,不管我們的切分規則設計的多麼的完美(實際上并不存在完美的切分規則),都可能造成之前的某些事務所涉及到的資料已經不在同一個mysqlserver中了。

在這樣的場景下,如果我們的應用程式仍然按照老的解決方案,那麼勢必需要引入分布式事務來解決。而在mysql各個版本中,隻有從mysql5.0開始以後的各個版本才開始對分布式事務提供支援,而且目前僅有innodb提供分布式事務支援。不僅如此,即使我們剛好使用了支援分布式事務的mysql版本,同時也是使用的innodb存儲引擎,分布式事務本身對于系統資源的消耗就是很大的,性能本身也并不是太高。而且引入分布式事務本身在異常處理方面就會帶來較多比較難控制的因素。

怎麼辦?其實我們可以可以通過一個變通的方法來解決這種問題,首先需要考慮的一件事情就是:是否資料庫是唯一一個能夠解決事務的地方呢?其實并不是這樣的,我們完全可以結合資料庫以及應用程式兩者來共同解決。各個資料庫解決自己身上的事務,然後通過應用程式來控制多個資料庫上面的事務。

也就是說,隻要我們願意,完全可以将一個跨多個資料庫的分布式事務分拆成多個僅處于單個資料庫上面的小事務,并通過應用程式來總控各個小事務。當然,這樣作的要求就是我們的俄應用程式必須要有足夠的健壯性,當然也會給應用程式帶來一些技術難度。

2.跨節點join的問題

上面介紹了可能引入分布式事務的問題,現在我們再看看需要跨節點join的問題。資料切分之後,可能會造成有些老的join語句無法繼續使用,因為join使用的資料源可能被切分到多個mysqlserver中了。

怎麼辦?這個問題從mysql資料庫角度來看,如果非得在資料庫端來直接解決的話,恐怕隻能通過mysql一種特殊的存儲引擎federated來解決了。federated存儲引擎是mysql解決類似于oracle的dblink之類問題的解決方案。和oracledblink的主要差別在于federated會儲存一份遠端表結構的定義資訊在本地。咋一看,federated确實是解決跨節點join非常好的解決方案。但是我們還應該清楚一點,那就似乎如果遠端的表結構發生了變更,本地的表定義資訊是不會跟着發生相應變化的。如果在更新遠端表結構的時候并沒有更新本地的federated表定義資訊,就很可能造成query運作出錯,無法得到正确的結果。

對待這類問題,我還是推薦通過應用程式來進行處理,先在驅動表所在的mysqlserver中取出相應的驅動結果集,然後根據驅動結果集再到被驅動表所在的mysqlserver中取出相應的資料。可能很多讀者朋友會認為這樣做對性能會産生一定的影響,是的,确實是會對性能有一定的負面影響,但是除了此法,基本上沒有太多其他更好的解決辦法了。而且,由于資料庫通過較好的擴充之後,每台mysqlserver的負載就可以得到較好的控制,單純針對單條query來說,其響應時間可能比不切分之前要提高一些,是以性能方面所帶來的負面影響也并不是太大。更何況,類似于這種需要跨節點join的需求也并不是太多,相對于總體性能而言,可能也隻是很小一部分而已。是以為了整體性能的考慮,偶爾犧牲那麼一點點,其實是值得的,畢竟系統優化本身就是存在很多取舍和平衡的過程。

3. 跨節點合并排序分頁問題

一旦進行了資料的水準切分之後,可能就并不僅僅隻有跨節點join無法正常運作,有些排序分頁的query語句的資料源可能也會被切分到多個節點,這樣造成的直接後果就是這些排序分頁query無法繼續正常運作。其實這和跨節點join是一個道理,資料源存在于多個節點上,要通過一個query來解決,就和跨節點join是一樣的操作。同樣federated也可以部分解決,當然存在的風險也一樣。

還是同樣的問題,怎麼辦?我同樣仍然繼續建議通過應用程式來解決。

如何解決?解決的思路大體上和跨節點join的解決類似,但是有一點和跨節點join不太一樣,join很多時候都有一個驅動與被驅動的關系,是以join本身涉及到的多個表之間的資料讀取一般都會存在一個順序關系。但是排序分頁就不太一樣了,排序分頁的資料源基本上可以說是一個表(或者一個結果集),本身并不存在一個順序關系,是以在從多個資料源取資料的過程是完全可以并行的。這樣,排序分頁資料的取數效率我們可以做的比跨庫join更高,是以帶來的性能損失相對的要更小,在有些情況下可能比在原來未進行資料切分的資料庫中效率更高了。當然,不論是跨節點join還是跨節點排序分頁,都會使我們的應用伺服器消耗更多的資源,尤其是記憶體資源,因為我們在讀取通路以及合并結果集的這個過程需要比原來處理更多的資料。

分析到這裡,可能很多讀者朋友會發現,上面所有的這些問題,我給出的建議基本上都是通過應用程式來解決。大家可能心裡開始犯嘀咕了,是不是因為我是dba,是以就很多事情都扔給應用架構師和開發人員了?

其實完全不是這樣,首先應用程式由于其特殊性,可以非常容易做到很好的擴充性,但是資料庫就不一樣,必須借助很多其他的方式才能做到擴充,而且在這個擴充過程中,很難避免帶來有些原來在集中式資料庫中可以解決但被切分開成一個資料庫叢集之後就成為一個難題的情況。要想讓系統整體得到最大限度的擴充,我們隻能讓應用程式做更多的事情,來解決資料庫叢集無法較好解決的問題。

通過資料切分技術将一個大的mysqlserver切分成多個小的mysqlserver,既解決了寫入性能瓶頸問題,同時也再一次提升了整個資料庫叢集的擴充性。不論是通過垂直切分,還是水準切分,都能夠讓系統遇到瓶頸的可能性更小。尤其是當我們使用垂直和水準相結合的切分方法之後,理論上将不會再遇到擴充瓶頸了。