数据库:数据库水平切分?垂直切分?整合方案?可能存在的问题?
前言
通過MySQLReplication功能所實現(xiàn)的擴展總是會受到數(shù)據(jù)庫大小的限制,一旦數(shù)據(jù)庫過于龐大,尤其是當(dāng)寫入過于頻繁,很難由一臺主機支撐的時候,我們還是會面臨到擴展瓶頸。這時候,我們就必須許找其他技術(shù)手段來解決這個瓶頸,那就是我們這一章所要介紹惡的數(shù)據(jù)切分技術(shù)。
不但要切開分到不同的DB,還要盡量減少各個DB之間的關(guān)聯(lián)
何謂數(shù)據(jù)切分
可能很多讀者朋友在網(wǎng)上或者雜志上面都已經(jīng)多次見到關(guān)于數(shù)據(jù)切分的相關(guān)文章了,只不過在有些文章中稱之為數(shù)據(jù)的Sharding。其實不管是稱之為數(shù)據(jù)的Sharding還是數(shù)據(jù)的切分,其概念都是一樣的。簡單來說,就是指通過某種特定的條件,將我們存放在同一個數(shù)據(jù)庫中的數(shù)據(jù)分散存放到多個數(shù)據(jù)庫(主機)上面,以達到分散單臺設(shè)備負載的效果。數(shù)據(jù)的切分同時還可以提高系統(tǒng)的總體可用性,因為單臺設(shè)備Crash之后,只有總體數(shù)據(jù)的某部分不可用,而不是所有的數(shù)據(jù)。
數(shù)據(jù)的切分(Sharding)根據(jù)其切分規(guī)則的類型,可以分為兩種切分模式。一種是按照不同的表(或者Schema)來切分到不同的數(shù)據(jù)庫(主機)之上,這種切可以稱之為數(shù)據(jù)的垂直(縱向)切分;另外一種則是根據(jù)表中的數(shù)據(jù)的邏輯關(guān)系,將同一個表中的數(shù)據(jù)按照某種條件拆分到多臺數(shù)據(jù)庫(主機)上面,這種切分稱之為數(shù)據(jù)的水平(橫向)切分。
垂直切分的最大特點就是規(guī)則簡單,實施也更為方便,尤其適合各業(yè)務(wù)之間的耦合度非常低,相互影響很小,業(yè)務(wù)邏輯非常清晰的系統(tǒng)。在這種系統(tǒng)中,可以很容易做到將不同業(yè)務(wù)模塊所使用的表分拆到不同的數(shù)據(jù)庫中。根據(jù)不同的表來進行拆分,對應(yīng)用程序的影響也更小,拆分規(guī)則也會比較簡單清晰。
水平切分于垂直切分相比,相對來說稍微復(fù)雜一些。因為要將同一個表中的不同數(shù)據(jù)拆分到不同的數(shù)據(jù)庫中,對于應(yīng)用程序來說,拆分規(guī)則本身就較根據(jù)表名來拆分更為復(fù)雜,后期的數(shù)據(jù)維護也會更為復(fù)雜一些。
當(dāng)我們某個(或者某些)表的數(shù)據(jù)量和訪問量特別的大,通過垂直切分將其放在獨立的設(shè)備上后仍然無法滿足性能要求,這時候我們就必須將垂直切分和水平切分相結(jié)合,先垂直切分,然后再水平切分,才能解決這種超大型表的性能問題。
下面我們就針對垂直、水平以及組合切分這三種數(shù)據(jù)切分方式的架構(gòu)實現(xiàn)及切分后數(shù)據(jù)的整合進行相應(yīng)的分析。
?
數(shù)據(jù)的垂直切分
我們先來看一下,數(shù)據(jù)的垂直切分到底是如何一個切分法的。數(shù)據(jù)的垂直切分,也可以稱之為縱向切分。將數(shù)據(jù)庫想象成為由很多個一大塊一大塊的“數(shù)據(jù)塊”(表)組成,我們垂直的將這些“數(shù)據(jù)塊”切開,然后將他們分散到多臺數(shù)據(jù)庫主機上面。這樣的切分方法就是一個垂直(縱向)的數(shù)據(jù)切分。
一個架構(gòu)設(shè)計較好的應(yīng)用系統(tǒng),其總體功能肯定是由很多個功能模塊所組成的,而每一個功能模塊所需要的數(shù)據(jù)對應(yīng)到數(shù)據(jù)庫中就是一個或者多個表。而在架構(gòu)設(shè)計中,各個功能模塊相互之間的交互點越統(tǒng)一越少,系統(tǒng)的耦合度就越低,系統(tǒng)各個模塊的維護性以及擴展性也就越好。這樣的系統(tǒng),實現(xiàn)數(shù)據(jù)的垂直切分也就越容易。
當(dāng)我們的功能模塊越清晰,耦合度越低,數(shù)據(jù)垂直切分的規(guī)則定義也就越容易。完全可以根據(jù)功能模塊來進行數(shù)據(jù)的切分,不同功能模塊的數(shù)據(jù)存放于不同的數(shù)據(jù)庫主機中,可以很容易就避免掉跨數(shù)據(jù)庫的Join存在,同時系統(tǒng)架構(gòu)也非常的清晰。
當(dāng)然,很難有系統(tǒng)能夠做到所有功能模塊所使用的表完全獨立,完全不需要訪問對方的表或者需要兩個模塊的表進行Join操作。這種情況下,我們就必須根據(jù)實際的應(yīng)用場景進行評估權(quán)衡。決定是遷就應(yīng)用程序?qū)⑿枰狫oin的表的相關(guān)某快都存放在同一個數(shù)據(jù)庫中,還是讓應(yīng)用程序做更多的事情,也就是程序完全通過模塊接口取得不同數(shù)據(jù)庫中的數(shù)據(jù),然后在程序中完成Join操作。
一般來說,如果是一個負載相對不是很大的系統(tǒng),而且表關(guān)聯(lián)又非常的頻繁,那可能數(shù)據(jù)庫讓步,將幾個相關(guān)模塊合并在一起減少應(yīng)用程序的工作的方案可以減少較多的工作量,是一個可行的方案。
當(dāng)然,通過數(shù)據(jù)庫的讓步,讓多個模塊集中共用數(shù)據(jù)源,實際上也是簡介的默許了各模塊架構(gòu)耦合度增大的發(fā)展,可能會讓以后的架構(gòu)越來越惡化。尤其是當(dāng)發(fā)展到一定階段之后,發(fā)現(xiàn)數(shù)據(jù)庫實在無法承擔(dān)這些表所帶來的壓力,不得不面臨再次切分的時候,所帶來的架構(gòu)改造成本可能會遠遠大于最初的時候。
所以,在數(shù)據(jù)庫進行垂直切分的時候,如何切分,切分到什么樣的程度,是一個比較考驗人的難題。只能在實際的應(yīng)用場景中通過平衡各方面的成本和收益,才能分析出一個真正適合自己的拆分方案。
比如在本書所使用示例系統(tǒng)的example數(shù)據(jù)庫,我們簡單的分析一下,然后再設(shè)計一個簡單的切分規(guī)則,進行一次垂直垂直拆分。
系統(tǒng)功能可以基本分為四個功能模塊:用戶,群組消息,相冊以及事件,分別對應(yīng)為如下這些表:
1. 用戶模塊表:user,user_profile,user_group,user_photo_album
2. 群組討論表:groups,group_message,group_message_content,top_message
3. 相冊相關(guān)表:photo,photo_album,photo_album_relation,photo_comment
4. 事件信息表:event
初略一看,沒有哪一個模塊可以脫離其他模塊獨立存在,模塊與模塊之間都存在著關(guān)系,莫非無法切分?
當(dāng)然不是,我們再稍微深入分析一下,可以發(fā)現(xiàn),雖然各個模塊所使用的表之間都有關(guān)聯(lián),但是關(guān)聯(lián)關(guān)系還算比較清晰,也比較簡單。
◆群組討論模塊和用戶模塊之間主要存在通過用戶或者是群組關(guān)系來進行關(guān)聯(lián)。一般關(guān)聯(lián)的時候都會是通過用戶的id或者nick_name以及group的id來進行關(guān)聯(lián),通過模塊之間的接口實現(xiàn)不會帶來太多麻煩;
◆相冊模塊僅僅與用戶模塊存在通過用戶的關(guān)聯(lián)。這兩個模塊之間的關(guān)聯(lián)基本就有通過用戶id關(guān)聯(lián)的內(nèi)容,簡單清晰,接口明確;
◆ 事件模塊與各個模塊可能都有關(guān)聯(lián),但是都只關(guān)注其各個模塊中對象的ID信息,同樣可以做到很容易分拆。
所以,我們第一步可以將數(shù)據(jù)庫按照功能模塊相關(guān)的表進行一次垂直拆分,每個模塊所涉及的表單獨到一個數(shù)據(jù)庫中,模塊與模塊之間的表關(guān)聯(lián)都在應(yīng)用系統(tǒng)端通過藉口來處理。如下圖所示:
通過這樣的垂直切分之后,之前只能通過一個數(shù)據(jù)庫來提供的服務(wù),就被分拆成四個數(shù)據(jù)庫來提供服務(wù),服務(wù)能力自然是增加幾倍了。
垂直切分的優(yōu)點
◆ 數(shù)據(jù)庫的拆分簡單明了,拆分規(guī)則明確;
◆ 應(yīng)用程序模塊清晰明確,整合容易;
◆ 數(shù)據(jù)維護方便易行,容易定位;
垂直切分的缺點
◆ 部分表關(guān)聯(lián)無法在數(shù)據(jù)庫級別完成,需要在程序中完成;
◆ 對于訪問極其頻繁且數(shù)據(jù)量超大的表仍然存在性能平靜,不一定能滿足要求;
◆ 事務(wù)處理相對更為復(fù)雜;
◆ 切分達到一定程度之后,擴展性會遇到限制;
◆ 過讀切分可能會帶來系統(tǒng)過渡復(fù)雜而難以維護。
針對于垂直切分可能遇到數(shù)據(jù)切分及事務(wù)問題,在數(shù)據(jù)庫層面實在是很難找到一個較好的處理方案。實際應(yīng)用案例中,數(shù)據(jù)庫的垂直切分大多是與應(yīng)用系統(tǒng)的模塊相對應(yīng),同一個模塊的數(shù)據(jù)源存放于同一個數(shù)據(jù)庫中,可以解決模塊內(nèi)部的數(shù)據(jù)關(guān)聯(lián)問題。而模塊與模塊之間,則通過應(yīng)用程序以服務(wù)接口方式來相互提供所需要的數(shù)據(jù)。雖然這樣做在數(shù)據(jù)庫的總體操作次數(shù)方面確實會有所增加,但是在系統(tǒng)整體擴展性以及架構(gòu)模塊化方面,都是有益的。可能在某些操作的單次響應(yīng)時間會稍有增加,但是系統(tǒng)的整體性能很可能反而會有一定的提升。而擴展瓶頸問題,就只能依靠下一節(jié)將要介紹的數(shù)據(jù)水平切分架構(gòu)來解決了。
?
數(shù)據(jù)的水平切分
上面一節(jié)分析介紹了數(shù)據(jù)的垂直切分,這一節(jié)再分析一下數(shù)據(jù)的水平切分。數(shù)據(jù)的垂直切分基本上可以簡單的理解為按照表按照模塊來切分數(shù)據(jù),而水平切分就不再是按照表或者是功能模塊來切分了。一般來說,簡單的水平切分主要是將某個訪問極其頻繁的表再按照某個字段的某種規(guī)則來分散到多個表之中,每個表中包含一部分數(shù)據(jù)。
簡單來說,我們可以將數(shù)據(jù)的水平切分理解為是按照數(shù)據(jù)行的切分,就是將表中的某些行切分到一個數(shù)據(jù)庫,而另外的某些行又切分到其他的數(shù)據(jù)庫中。當(dāng)然,為了能夠比較容易的判定各行數(shù)據(jù)被切分到哪個數(shù)據(jù)庫中了,切分總是都需要按照某種特定的規(guī)則來進行的。如根據(jù)某個數(shù)字類型字段基于特定數(shù)目取模,某個時間類型字段的范圍,或者是某個字符類型字段的hash值。如果整個系統(tǒng)中大部分核心表都可以通過某個字段來進行關(guān)聯(lián),那這個字段自然是一個進行水平分區(qū)的上上之選了,當(dāng)然,非常特殊無法使用就只能另選其他了。
一般來說,像現(xiàn)在互聯(lián)網(wǎng)非常火爆的Web2.0類型的網(wǎng)站,基本上大部分數(shù)據(jù)都能夠通過會員用戶信息關(guān)聯(lián)上,可能很多核心表都非常適合通過會員ID來進行數(shù)據(jù)的水平切分。而像論壇社區(qū)討論系統(tǒng),就更容易切分了,非常容易按照論壇編號來進行數(shù)據(jù)的水平切分。切分之后基本上不會出現(xiàn)各個庫之間的交互。
如我們的示例系統(tǒng),所有數(shù)據(jù)都是和用戶關(guān)聯(lián)的,那么我們就可以根據(jù)用戶來進行水平拆分,將不同用戶的數(shù)據(jù)切分到不同的數(shù)據(jù)庫中。當(dāng)然,唯一有點區(qū)別的是用戶模塊中的groups表和用戶沒有直接關(guān)系,所以groups不能根據(jù)用戶來進行水平拆分。對于這種特殊情況下的表,我們完全可以獨立出來,單獨放在一個獨立的數(shù)據(jù)庫中。其實這個做法可以說是利用了前面一節(jié)所介紹的“數(shù)據(jù)的垂直切分”方法,我將在下一節(jié)中更為詳細的介紹這種垂直切分與水平切分同時使用的聯(lián)合切分方法。
所以,對于我們的示例數(shù)據(jù)庫來說,大部分的表都可以根據(jù)用戶ID來進行水平的切分。不同用戶相關(guān)的數(shù)據(jù)進行切分之后存放在不同的數(shù)據(jù)庫中。如將所有用戶ID通過2取模然后分別存放于兩個不同的數(shù)據(jù)庫中。每個和用戶ID關(guān)聯(lián)上的表都可以這樣切分。這樣,基本上每個用戶相關(guān)的數(shù)據(jù),都在同一個數(shù)據(jù)庫中,即使是需要關(guān)聯(lián),也可以非常簡單的關(guān)聯(lián)上。
我們可以通過下圖來更為直觀的展示水平切分相關(guān)信息:水平切分的優(yōu)點
水平切分優(yōu)點
◆ 表關(guān)聯(lián)基本能夠在數(shù)據(jù)庫端全部完成;
◆ 不會存在某些超大型數(shù)據(jù)量和高負載的表遇到瓶頸的問題;
◆ 應(yīng)用程序端整體架構(gòu)改動相對較少;
◆ 事務(wù)處理相對簡單;
◆ 只要切分規(guī)則能夠定義好,基本上較難遇到擴展性限制;
水平切分的缺點
◆ 切分規(guī)則相對更為復(fù)雜,很難抽象出一個能夠滿足整個數(shù)據(jù)庫的切分規(guī)則;
◆ 后期數(shù)據(jù)的維護難度有所增加,人為手工定位數(shù)據(jù)更困難;
◆ 應(yīng)用系統(tǒng)各模塊耦合度較高,可能會對后面數(shù)據(jù)的遷移拆分造成一定的困難。
?
垂直與水平切分的聯(lián)合使用
考慮成本,最先會使用垂直切分,后面再進行水平切分,組合使用
上面兩節(jié)內(nèi)容中,我們分別,了解了“垂直”和“水平”這兩種切分方式的實現(xiàn)以及切分之后的架構(gòu)信息,同時也分析了兩種架構(gòu)各自的優(yōu)缺點。但是在實際的應(yīng)用場景中,除了那些負載并不是太大,業(yè)務(wù)邏輯也相對較簡單的系統(tǒng)可以通過上面兩種切分方法之一來解決擴展性問題之外,恐怕其他大部分業(yè)務(wù)邏輯稍微復(fù)雜一點,系統(tǒng)負載大一些的系統(tǒng),都無法通過上面任何一種數(shù)據(jù)的切分方法來實現(xiàn)較好的擴展性,而需要將上述兩種切分方法結(jié)合使用,不同的場景使用不同的切分方法。
在這一節(jié)中,我將結(jié)合垂直切分和水平切分各自的優(yōu)缺點,進一步完善我們的整體架構(gòu),讓系統(tǒng)的擴展性進一步提高。
一般來說,我們數(shù)據(jù)庫中的所有表很難通過某一個(或少數(shù)幾個)字段全部關(guān)聯(lián)起來,所以很難簡單的僅僅通過數(shù)據(jù)的水平切分來解決所有問題。(如果存在,每張表都按照這個字段進行水平切分,那么很容易將該字段相關(guān)聯(lián)的數(shù)據(jù)段分在一個DB里面,減少了DB之間的關(guān)聯(lián))而垂直切分也只能解決部分問題,對于那些負載非常高的系統(tǒng),即使僅僅只是單個表都無法通過單臺數(shù)據(jù)庫主機來承擔(dān)其負載。我們必須結(jié)合“垂直”和“水平”兩種切分方式同時使用,充分利用兩者的優(yōu)點,避開其缺點。
每一個應(yīng)用系統(tǒng)的負載都是一步一步增長上來的,在開始遇到性能瓶頸的時候,大多數(shù)架構(gòu)師和DBA都會選擇先進行數(shù)據(jù)的垂直拆分,因為這樣的成本最先,最符合這個時期所追求的最大投入產(chǎn)出比。然而,隨著業(yè)務(wù)的不斷擴張,系統(tǒng)負載的持續(xù)增長,在系統(tǒng)穩(wěn)定一段時期之后,經(jīng)過了垂直拆分之后的數(shù)據(jù)庫集群可能又再一次不堪重負,遇到了性能瓶頸。
這時候我們該如何抉擇?是再次進一步細分模塊呢,還是尋求其他的辦法來解決?如果我們再一次像最開始那樣繼續(xù)細分模塊,進行數(shù)據(jù)的垂直切分,那我們可能在不久的將來,又會遇到現(xiàn)在所面對的同樣的問題。而且隨著模塊的不斷的細化,應(yīng)用系統(tǒng)的架構(gòu)也會越來越復(fù)雜,整個系統(tǒng)很可能會出現(xiàn)失控的局面。
這時候我們就必須要通過數(shù)據(jù)的水平切分的優(yōu)勢,來解決這里所遇到的問題。而且,我們完全不必要在使用數(shù)據(jù)水平切分的時候,推倒之前進行數(shù)據(jù)垂直切分的成果,而是在其基礎(chǔ)上利用水平切分的優(yōu)勢來避開垂直切分的弊端,解決系統(tǒng)復(fù)雜性不斷擴大的問題。而水平拆分的弊端(規(guī)則難以統(tǒng)一)也已經(jīng)被之前的垂直切分解決掉了,讓水平拆分可以進行的得心應(yīng)手。
對于我們的示例數(shù)據(jù)庫,假設(shè)在最開始,我們進行了數(shù)據(jù)的垂直切分,然而隨著業(yè)務(wù)的不斷增長,數(shù)據(jù)庫系統(tǒng)遇到了瓶頸,我們選擇重構(gòu)數(shù)據(jù)庫集群的架構(gòu)。如何重構(gòu)?考慮到之前已經(jīng)做好了數(shù)據(jù)的垂直切分,而且模塊結(jié)構(gòu)清晰明確。而業(yè)務(wù)增長的勢頭越來越猛,即使現(xiàn)在進一步再次拆分模塊,也堅持不了太久。我們選擇了在垂直切分的基礎(chǔ)上再進行水平拆分。
在經(jīng)歷過垂直拆分后的各個數(shù)據(jù)庫集群中的每一個都只有一個功能模塊,而每個功能模塊中的所有表基本上都會與某個字段進行關(guān)聯(lián)。如用戶模塊全部都可以通過用戶ID進行切分,群組討論模塊則都通過群組ID來切分,相冊模塊則根據(jù)相冊ID來進切分,最后的事件通知信息表考慮到數(shù)據(jù)的時限性(僅僅只會訪問最近某個事件段的信息),則考慮按時間來切分。
下圖展示了切分后的整個架構(gòu):
實際上,在很多大型的應(yīng)用系統(tǒng)中,垂直切分和水平切這兩種數(shù)據(jù)的切分方法基本上都是并存的,而且經(jīng)常在不斷的交替進行,以不斷的增加系統(tǒng)的擴展能力。我們在應(yīng)對不同的應(yīng)用場景的時候,也需要充分考慮到這兩種切分方法各自的局限,以及各自的優(yōu)勢,在不同的時期(負載壓力)使用不同的結(jié)合方式。
聯(lián)合切分的優(yōu)點
◆ 可以充分利用垂直切分和水平切分各自的優(yōu)勢而避免各自的缺陷;
◆ 讓系統(tǒng)擴展性得到最大化提升;
聯(lián)合切分的缺點
◆ 數(shù)據(jù)庫系統(tǒng)架構(gòu)比較復(fù)雜,維護難度更大;
◆ 應(yīng)用程序架構(gòu)也相對更復(fù)雜;
數(shù)據(jù)切分及整合方案
通過前面的章節(jié),我們已經(jīng)很清楚了通過數(shù)據(jù)庫的數(shù)據(jù)切分可以極大的提高系統(tǒng)的擴展性。但是,數(shù)據(jù)庫中的數(shù)據(jù)在經(jīng)過垂直和(或)水平切分被存放在不同的數(shù)據(jù)庫主機之后,應(yīng)用系統(tǒng)面臨的最大問題就是如何來讓這些數(shù)據(jù)源得到較好的整合,可能這也是很多讀者朋友非常關(guān)心的一個問題。這一節(jié)我們主要針對的內(nèi)容就是分析可以使用的各種可以幫助我們實現(xiàn)數(shù)據(jù)切分以及數(shù)據(jù)整合的整體解決方案。
?
應(yīng)用系統(tǒng)面臨的最大問題就是如何來讓這些數(shù)據(jù)源得到較好的整合?
數(shù)據(jù)的整合很難依靠數(shù)據(jù)庫本身來達到這個效果,雖然MySQL存在Federated存儲引擎,可以解決部分類似的問題,但是在實際應(yīng)用場景中卻很難較好的運用。那我們該如何來整合這些分散在各個MySQL主機上面的數(shù)據(jù)源呢?
總的來說,存在兩種解決思路:
1. 在每個應(yīng)用程序模塊中配置管理自己需要的一個(或者多個)數(shù)據(jù)源,直接訪問各個數(shù)據(jù)庫,在模塊內(nèi)完成數(shù)據(jù)的整合;
2. 通過中間代理層來統(tǒng)一管理所有的數(shù)據(jù)源,后端數(shù)據(jù)庫集群對前端應(yīng)用程序透明;(MyCat)
可能90%以上的人在面對上面這兩種解決思路的時候都會傾向于選擇第二種,尤其是系統(tǒng)不斷變得龐大復(fù)雜的時候。確實,這是一個非常正確的選擇,雖然短期內(nèi)需要付出的成本可能會相對更大一些,但是對整個系統(tǒng)的擴展性來說,是非常有幫助的。
所以,對于第一種解決思路我這里就不準備過多的分析,下面我重點分析一下在第二種解決思路中的一些解決方案。
★ 自行開發(fā)中間代理層
在決定選擇通過數(shù)據(jù)庫的中間代理層來解決數(shù)據(jù)源整合的架構(gòu)方向之后,有不少公司(或者企業(yè))選擇了通過自行開發(fā)符合自身應(yīng)用特定場景的代理層應(yīng)用程序。
通過自行開發(fā)中間代理層可以最大程度的應(yīng)對自身應(yīng)用的特定,最大化的定制很多個性化需求,在面對變化的時候也可以靈活的應(yīng)對。這應(yīng)該說是自行開發(fā)代理層最大的優(yōu)勢了。
當(dāng)然,選擇自行開發(fā),享受讓個性化定制最大化的樂趣的同時,自然也需要投入更多的成本來進行前期研發(fā)以及后期的持續(xù)升級改進工作,而且本身的技術(shù)門檻可能也比簡單的Web應(yīng)用要更高一些。所以,在決定選擇自行開發(fā)之前,還是需要進行比較全面的評估為好。
由于自行開發(fā)更多時候考慮的是如何更好的適應(yīng)自身應(yīng)用系統(tǒng),應(yīng)對自身的業(yè)務(wù)場景,所以這里也不好分析太多。后面我們主要分析一下當(dāng)前比較流行的幾種數(shù)據(jù)源整合解決方案。
★利用MySQLProxy實現(xiàn)數(shù)據(jù)切分及整合
MySQLProxy是MySQL官方提供的一個數(shù)據(jù)庫代理層產(chǎn)品,和MySQLServer一樣,同樣是一個基于GPL開源協(xié)議的開源產(chǎn)品。可用來監(jiān)視、分析或者傳輸他們之間的通訊信息。他的靈活性允許你最大限度的使用它,目前具備的功能主要有連接路由,Query分析,Query過濾和修改,負載均衡,以及基本的HA機制等。
實際上,MySQLProxy本身并不具有上述所有的這些功能,而是提供了實現(xiàn)上述功能的基礎(chǔ)。要實現(xiàn)這些功能,還需要通過我們自行編寫LUA腳本來實現(xiàn)。
MySQLProxy實際上是在客戶端請求與MySQLServer之間建立了一個連接池。所有客戶端請求都是發(fā)向MySQLProxy,然后經(jīng)由MySQLProxy進行相應(yīng)的分析,判斷出是讀操作還是寫操作,分發(fā)至對應(yīng)的MySQLServer上。對于多節(jié)點Slave集群,也可以起做到負載均衡的效果。以下是MySQLProxy的基本架構(gòu)圖:
通過上面的架構(gòu)簡圖,我們可以很清晰的看出MySQLProxy在實際應(yīng)用中所處的位置,以及能做的基本事情。關(guān)于MySQLProxy更為詳細的實施細則在MySQL官方文檔中有非常詳細的介紹和示例,感興趣的讀者朋友可以直接從MySQL官方網(wǎng)站免費下載或者在線閱讀,我這里就不累述浪費紙張了。
★利用Amoeba實現(xiàn)數(shù)據(jù)切分及整合
Amoeba是一個基于Java開發(fā)的,專注于解決分布式數(shù)據(jù)庫數(shù)據(jù)源整合Proxy程序的開源框架,基于GPL3開源協(xié)議。目前,Amoeba已經(jīng)具有Query路由,Query過濾,讀寫分離,負載均衡以及HA機制等相關(guān)內(nèi)容。
Amoeba 主要解決的以下幾個問題:
1. 數(shù)據(jù)切分后復(fù)雜數(shù)據(jù)源整合;
2. 提供數(shù)據(jù)切分規(guī)則并降低數(shù)據(jù)切分規(guī)則給數(shù)據(jù)庫帶來的影響;
3. 降低數(shù)據(jù)庫與客戶端的連接數(shù);
4. 讀寫分離路由;
我們可以看出,Amoeba所做的事情,正好就是我們通過數(shù)據(jù)切分來提升數(shù)據(jù)庫的擴展性所需要的。
Amoeba并不是一個代理層的Proxy程序,而是一個開發(fā)數(shù)據(jù)庫代理層Proxy程序的開發(fā)框架,目前基于Amoeba所開發(fā)的Proxy程序有AmoebaForMySQL和AmoebaForAladin兩個。
AmoebaForMySQL主要是專門針對MySQL數(shù)據(jù)庫的解決方案,前端應(yīng)用程序請求的協(xié)議以及后端連接的數(shù)據(jù)源數(shù)據(jù)庫都必須是MySQL。對于客戶端的任何應(yīng)用程序來說,AmoebaForMySQL和一個MySQL數(shù)據(jù)庫沒有什么區(qū)別,任何使用MySQL協(xié)議的客戶端請求,都可以被AmoebaForMySQL解析并進行相應(yīng)的處理。下如可以告訴我們AmoebaForMySQL的架構(gòu)信息(出自Amoeba開發(fā)者博客):
AmoebaForAladin則是一個適用更為廣泛,功能更為強大的Proxy程序。他可以同時連接不同數(shù)據(jù)庫的數(shù)據(jù)源為前端應(yīng)用程序提供服務(wù),但是僅僅接受符合MySQL協(xié)議的客戶端應(yīng)用程序請求。也就是說,只要前端應(yīng)用程序通過MySQL協(xié)議連接上來之后,AmoebaForAladin會自動分析Query語句,根據(jù)Query語句中所請求的數(shù)據(jù)來自動識別出該所Query的數(shù)據(jù)源是在什么類型數(shù)據(jù)庫的哪一個物理主機上面。下圖展示了AmoebaForAladin的架構(gòu)細節(jié)(出自Amoeba開發(fā)者博客):
咋一看,兩者好像完全一樣嘛。細看之后,才會發(fā)現(xiàn)兩者主要的區(qū)別僅在于通過MySQLProtocalAdapter處理之后,根據(jù)分析結(jié)果判斷出數(shù)據(jù)源數(shù)據(jù)庫,然后選擇特定的JDBC驅(qū)動和相應(yīng)協(xié)議連接后端數(shù)據(jù)庫。
其實通過上面兩個架構(gòu)圖大家可能也已經(jīng)發(fā)現(xiàn)了Amoeba的特點了,他僅僅只是一個開發(fā)框架,我們除了選擇他已經(jīng)提供的ForMySQL和ForAladin這兩款產(chǎn)品之外,還可以基于自身的需求進行相應(yīng)的二次開發(fā),得到更適應(yīng)我們自己應(yīng)用特點的Proxy程序。
當(dāng)對于使用MySQL數(shù)據(jù)庫來說,不論是AmoebaForMySQL還是AmoebaForAladin都可以很好的使用。當(dāng)然,考慮到任何一個系統(tǒng)越是復(fù)雜,其性能肯定就會有一定的損失,維護成本自然也會相對更高一些。所以,對于僅僅需要使用MySQL數(shù)據(jù)庫的時候,我還是建議使用AmoebaForMySQL。
AmoebaForMySQL的使用非常簡單,所有的配置文件都是標準的XML文件,總共有四個配置文件。分別為:
◆amoeba.xml:主配置文件,配置所有數(shù)據(jù)源以及Amoeba自身的參數(shù)設(shè)置;
◆rule.xml:配置所有Query路由規(guī)則的信息;
◆functionMap.xml:配置用于解析Query中的函數(shù)所對應(yīng)的Java實現(xiàn)類;
◆ rullFunctionMap.xml:配置路由規(guī)則中需要使用到的特定函數(shù)的實現(xiàn)類;
如果您的規(guī)則不是太復(fù)雜,基本上僅需要使用到上面四個配置文件中的前面兩個就可完成所有工作。Proxy程序常用的功能如讀寫分離,負載均衡等配置都在amoeba.xml中進行。此外,Amoeba已經(jīng)支持了實現(xiàn)數(shù)據(jù)的垂直切分和水平切分的自動路由,路由規(guī)則可以在rule.xml進行設(shè)置。
目前Amoeba少有欠缺的主要就是其在線管理功能以及對事務(wù)的支持了,曾經(jīng)在與相關(guān)開發(fā)者的溝通過程中提出過相關(guān)的建議,希望能夠提供一個可以進行在線維護管理的命令行管理工具,方便在線維護使用,得到的反饋是管理專門的管理模塊已經(jīng)納入開發(fā)日程了。另外在事務(wù)支持方面暫時還是Amoeba無法做到的,即使客戶端應(yīng)用在提交給Amoeba的請求是包含事務(wù)信息的,Amoeba也會忽略事務(wù)相關(guān)信息。當(dāng)然,在經(jīng)過不斷完善之后,我相信事務(wù)支持肯定是Amoeba重點考慮增加的feature。
關(guān)于Amoeba更為詳細的使用方法讀者朋友可以通過Amoeba開發(fā)者博客(http://amoeba.sf.net)上面提供的使用手冊獲取,這里就不再細述了。
★利用HiveDB實現(xiàn)數(shù)據(jù)切分及整合
和前面的MySQLProxy以及Amoeba一樣,HiveDB同樣是一個基于Java針對MySQL數(shù)據(jù)庫的提供數(shù)據(jù)切分及整合的開源框架,只是目前的HiveDB僅僅支持數(shù)據(jù)的水平切分。主要解決大數(shù)據(jù)量下數(shù)據(jù)庫的擴展性及數(shù)據(jù)的高性能訪問問題,同時支持數(shù)據(jù)的冗余及基本的HA機制。
HiveDB的實現(xiàn)機制與MySQLProxy和Amoeba有一定的差異,他并不是借助MySQL的Replication功能來實現(xiàn)數(shù)據(jù)的冗余,而是自行實現(xiàn)了數(shù)據(jù)冗余機制,而其底層主要是基于HibernateShards來實現(xiàn)的數(shù)據(jù)切分工作。
在HiveDB中,通過用戶自定義的各種Partitionkeys(其實就是制定數(shù)據(jù)切分規(guī)則),將數(shù)據(jù)分散到多個MySQLServer中。在訪問的時候,在運行Query請求的時候,會自動分析過濾條件,并行從多個MySQLServer中讀取數(shù)據(jù),并合并結(jié)果集返回給客戶端應(yīng)用程序。
單純從功能方面來講,HiveDB可能并不如MySQLProxy和Amoeba那樣強大,但是其數(shù)據(jù)切分的思路與前面二者并無本質(zhì)差異。此外,HiveDB并不僅僅只是一個開源愛好者所共享的內(nèi)容,而是存在商業(yè)公司支持的開源項目。
下面是HiveDB官方網(wǎng)站上面一章圖片,描述了HiveDB如何來組織數(shù)據(jù)的基本信息,雖然不能詳細的表現(xiàn)出太多架構(gòu)方面的信息,但是也基本可以展示出其在數(shù)據(jù)切分方面獨特的一面了。
★ 其他實現(xiàn)數(shù)據(jù)切分及整合的解決方案
除了上面介紹的幾個數(shù)據(jù)切分及整合的整體解決方案之外,還存在很多其他同樣提供了數(shù)據(jù)切分與整合的解決方案。如基于MySQLProxy的基礎(chǔ)上做了進一步擴展的HSCALE,通過Rails構(gòu)建的SpockProxy,以及基于Pathon的Pyshards等等。
不管大家選擇使用哪一種解決方案,總體設(shè)計思路基本上都不應(yīng)該會有任何變化,那就是通過數(shù)據(jù)的垂直和水平切分,增強數(shù)據(jù)庫的整體服務(wù)能力,讓應(yīng)用系統(tǒng)的整體擴展能力盡可能的提升,擴展方式盡可能的便捷。
只要我們通過中間層Proxy應(yīng)用程序較好的解決了數(shù)據(jù)切分和數(shù)據(jù)源整合問題,那么數(shù)據(jù)庫的線性擴展能力將很容易做到像我們的應(yīng)用程序一樣方便,只需要通過添加廉價的PCServer服務(wù)器,即可線性增加數(shù)據(jù)庫集群的整體服務(wù)能力,讓數(shù)據(jù)庫不再輕易成為應(yīng)用系統(tǒng)的性能瓶頸。
數(shù)據(jù)切分與整合可能存在的問題
這里,大家應(yīng)該對數(shù)據(jù)切分與整合的實施有了一定的認識了,或許很多讀者朋友都已經(jīng)根據(jù)各種解決方案各自特性的優(yōu)劣基本選定了適合于自己應(yīng)用場景的方案,后面的工作主要就是實施準備了。
在實施數(shù)據(jù)切分方案之前,有些可能存在的問題我們還是需要做一些分析的。一般來說,我們可能遇到的問題主要會有以下幾點:
◆ 引入分布式事務(wù)的問題;
◆跨節(jié)點Join的問題;(DB之間的表鏈接)
◆ 跨節(jié)點合并排序分頁問題;
?
1. 引入分布式事務(wù)的問題
一旦數(shù)據(jù)進行切分被分別存放在多個MySQLServer中之后,不管我們的切分規(guī)則設(shè)計的多么的完美(實際上并不存在完美的切分規(guī)則),都可能造成之前的某些事務(wù)所涉及到的數(shù)據(jù)已經(jīng)不在同一個MySQLServer中了。
在這樣的場景下,如果我們的應(yīng)用程序仍然按照老的解決方案,那么勢必需要引入分布式事務(wù)來解決。而在MySQL各個版本中,只有從MySQL5.0開始以后的各個版本才開始對分布式事務(wù)提供支持,而且目前僅有Innodb提供分布式事務(wù)支持。不僅如此,即使我們剛好使用了支持分布式事務(wù)的MySQL版本,同時也是使用的Innodb存儲引擎,分布式事務(wù)本身對于系統(tǒng)資源的消耗就是很大的,性能本身也并不是太高。而且引入分布式事務(wù)本身在異常處理方面就會帶來較多比較難控制的因素。
怎么辦?其實我們可以可以通過一個變通的方法來解決這種問題,首先需要考慮的一件事情就是:是否數(shù)據(jù)庫是唯一一個能夠解決事務(wù)的地方呢?其實并不是這樣的,我們完全可以結(jié)合數(shù)據(jù)庫以及應(yīng)用程序兩者來共同解決。
各個數(shù)據(jù)庫解決自己身上的事務(wù),然后通過應(yīng)用程序來控制多個數(shù)據(jù)庫上面的事務(wù)。
也就是說,只要我們愿意,完全可以將一個跨多個數(shù)據(jù)庫的分布式事務(wù)分拆成多個僅處于單個數(shù)據(jù)庫上面的小事務(wù),并通過應(yīng)用程序來總控各個小事務(wù)。當(dāng)然,這樣作的要求就是我們的俄應(yīng)用程序必須要有足夠的健壯性,當(dāng)然也會給應(yīng)用程序帶來一些技術(shù)難度。
?
2.跨節(jié)點Join的問題
上面介紹了可能引入分布式事務(wù)的問題,現(xiàn)在我們再看看需要跨節(jié)點Join的問題。數(shù)據(jù)切分之后,可能會造成有些老的Join語句無法繼續(xù)使用,因為Join使用的數(shù)據(jù)源可能被切分到多個MySQLServer中了。
怎么辦?這個問題從MySQL數(shù)據(jù)庫角度來看,如果非得在數(shù)據(jù)庫端來直接解決的話,恐怕只能通過MySQL一種特殊的存儲引擎Federated來解決了。Federated存儲引擎是MySQL解決類似于Oracle的DBLink之類問題的解決方案。和OracleDBLink的主要區(qū)別在于Federated會保存一份遠端表結(jié)構(gòu)的定義信息在本地。咋一看,Federated確實是解決跨節(jié)點Join非常好的解決方案。但是我們還應(yīng)該清楚一點,那就似乎如果遠端的表結(jié)構(gòu)發(fā)生了變更,本地的表定義信息是不會跟著發(fā)生相應(yīng)變化的。如果在更新遠端表結(jié)構(gòu)的時候并沒有更新本地的Federated表定義信息,就很可能造成Query運行出錯,無法得到正確的結(jié)果。
對待這類問題,我還是推薦通過應(yīng)用程序來進行處理,先在驅(qū)動表所在的MySQLServer中取出相應(yīng)的驅(qū)動結(jié)果集,然后根據(jù)驅(qū)動結(jié)果集再到被驅(qū)動表所在的MySQLServer中取出相應(yīng)的數(shù)據(jù)。可能很多讀者朋友會認為這樣做對性能會產(chǎn)生一定的影響,是的,確實是會對性能有一定的負面影響,但是除了此法,基本上沒有太多其他更好的解決辦法了。而且,由于數(shù)據(jù)庫通過較好的擴展之后,每臺MySQLServer的負載就可以得到較好的控制,單純針對單條Query來說,其響應(yīng)時間可能比不切分之前要提高一些,所以性能方面所帶來的負面影響也并不是太大。更何況,類似于這種需要跨節(jié)點Join的需求也并不是太多,相對于總體性能而言,可能也只是很小一部分而已。所以為了整體性能的考慮,偶爾犧牲那么一點點,其實是值得的,畢竟系統(tǒng)優(yōu)化本身就是存在很多取舍和平衡的過程。
?
3. 跨節(jié)點合并排序分頁問題
一旦進行了數(shù)據(jù)的水平切分之后,可能就并不僅僅只有跨節(jié)點Join無法正常運行,有些排序分頁的Query語句的數(shù)據(jù)源可能也會被切分到多個節(jié)點,這樣造成的直接后果就是這些排序分頁Query無法繼續(xù)正常運行。其實這和跨節(jié)點Join是一個道理,數(shù)據(jù)源存在于多個節(jié)點上,要通過一個Query來解決,就和跨節(jié)點Join是一樣的操作。同樣Federated也可以部分解決,當(dāng)然存在的風(fēng)險也一樣。
還是同樣的問題,怎么辦?我同樣仍然繼續(xù)建議通過應(yīng)用程序來解決。
如何解決?解決的思路大體上和跨節(jié)點Join的解決類似,但是有一點和跨節(jié)點Join不太一樣,Join很多時候都有一個驅(qū)動與被驅(qū)動的關(guān)系,所以Join本身涉及到的多個表之間的數(shù)據(jù)讀取一般都會存在一個順序關(guān)系,串行。但是排序分頁就不太一樣了,排序分頁的數(shù)據(jù)源基本上可以說是一個表(或者一個結(jié)果集),本身并不存在一個順序關(guān)系,所以在從多個數(shù)據(jù)源取數(shù)據(jù)的過程是完全可以并行的。這樣,排序分頁數(shù)據(jù)的取數(shù)效率我們可以做的比跨庫Join更高,所以帶來的性能損失相對的要更小,在有些情況下可能比在原來未進行數(shù)據(jù)切分的數(shù)據(jù)庫中效率更高了。當(dāng)然,不論是跨節(jié)點Join還是跨節(jié)點排序分頁,都會使我們的應(yīng)用服務(wù)器消耗更多的資源,尤其是內(nèi)存資源,因為我們在讀取訪問以及合并結(jié)果集的這個過程需要比原來處理更多的數(shù)據(jù)。
分析到這里,可能很多讀者朋友會發(fā)現(xiàn),上面所有的這些問題,我給出的建議基本上都是通過應(yīng)用程序來解決。大家可能心里開始犯嘀咕了,是不是因為我是DBA,所以就很多事情都扔給應(yīng)用架構(gòu)師和開發(fā)人員了?
其實完全不是這樣,首先應(yīng)用程序由于其特殊性,可以非常容易做到很好的擴展性,但是數(shù)據(jù)庫就不一樣,必須借助很多其他的方式才能做到擴展,而且在這個擴展過程中,很難避免帶來有些原來在集中式數(shù)據(jù)庫中可以解決但被切分開成一個數(shù)據(jù)庫集群之后就成為一個難題的情況。要想讓系統(tǒng)整體得到最大限度的擴展,我們只能讓應(yīng)用程序做更多的事情,來解決數(shù)據(jù)庫集群無法較好解決的問題。
?
小結(jié)
通過數(shù)據(jù)切分技術(shù)將一個大的MySQLServer切分成多個小的MySQLServer,既解決了寫入性能瓶頸問題,同時也再一次提升了整個數(shù)據(jù)庫集群的擴展性。不論是通過垂直切分,還是水平切分,都能夠讓系統(tǒng)遇到瓶頸的可能性更小。尤其是當(dāng)我們使用垂直和水平相結(jié)合的切分方法之后,理論上將不會再遇到擴展瓶頸了。
總結(jié)
以上是生活随笔為你收集整理的数据库:数据库水平切分?垂直切分?整合方案?可能存在的问题?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据库:B+树索引和Hash索引得区别
- 下一篇: 数据库:数据库的连接池原理及实现