久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

MySQL數(shù)據(jù)庫(kù)中怎么實(shí)現(xiàn)水平切分

共計(jì) 10436 個(gè)字符,預(yù)計(jì)需要花費(fèi) 27 分鐘才能閱讀完成。

MySQL 數(shù)據(jù)庫(kù)中怎么實(shí)現(xiàn)水平切分,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面丸趣 TV 小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

隨著互聯(lián)網(wǎng)應(yīng)用的廣泛普及,海量數(shù)據(jù)的存儲(chǔ)和訪問成為了系統(tǒng)設(shè)計(jì)的瓶頸問題。對(duì)于一個(gè)大型的互聯(lián)網(wǎng)應(yīng)用,每天幾十億的 PV 無疑對(duì)數(shù)據(jù)庫(kù)造成了相當(dāng)高的負(fù)載。對(duì)于系統(tǒng)的穩(wěn)定性和擴(kuò)展性造成了極大的問題。通過數(shù)據(jù)切分來提高網(wǎng)站性能,橫向擴(kuò)展數(shù)據(jù)層已經(jīng)成為架構(gòu)研發(fā)人員首選的方式。水平切分?jǐn)?shù)據(jù)庫(kù),可以降低單臺(tái)機(jī)器的負(fù)載,同時(shí)最大限度的降低了了宕機(jī)造成的損失。通過負(fù)載均衡策略,有效的降低了單臺(tái)機(jī)器的訪問負(fù)載,降低了宕機(jī)的可能性;通過集群方案,解決了數(shù)據(jù)庫(kù)宕機(jī)帶來的單點(diǎn)數(shù)據(jù)庫(kù)不能訪問的問題;通過讀寫分離策略更是最大限度了提高了應(yīng)用中讀取(Read)數(shù)據(jù)的速度和并發(fā)量。目前國(guó)內(nèi)的大型互聯(lián)網(wǎng)應(yīng)用中,大量的采用了這樣的數(shù)據(jù)切分方案,Taobao,Alibaba,Tencent,它們大都實(shí)現(xiàn)了自己的分布式數(shù)據(jù)訪問層(DDAL)。以實(shí)現(xiàn)方式和實(shí)現(xiàn)的層次來劃分,大概分為兩個(gè)層次(Java 應(yīng)用為例):JDBC 層的封裝,ORM 框架層的實(shí)現(xiàn)。就 JDBC 層的直接封裝而言,現(xiàn)在國(guó)內(nèi)發(fā)展較好的一個(gè)項(xiàng)目是被稱作“變形蟲”(Amoeba)的項(xiàng)目,由阿里集團(tuán)的研究院開發(fā),現(xiàn)在仍然處于測(cè)試階段(beta 版),其運(yùn)行效率和生產(chǎn)時(shí)效性有待考究。就 ORM 框架層的實(shí)現(xiàn)而言,比如 Taobao 的基于 ibatis 和 Spring 的的分布式數(shù)據(jù)訪問層,已有多年的應(yīng)用,運(yùn)行效率和生產(chǎn)實(shí)效性得到了開發(fā)人員和用戶的肯定。本文就是以 ORM 框架層為基礎(chǔ)而實(shí)現(xiàn)的分布式數(shù)據(jù)訪問層。本課題的難點(diǎn)在于分庫(kù)后,路由規(guī)則的制定和選擇以及后期的擴(kuò)展性,比如:如何做到用最少的數(shù)據(jù)遷移量,達(dá)到擴(kuò)充數(shù)據(jù)庫(kù)容量(增加機(jī)器節(jié)點(diǎn))的目的。核心問題將圍繞數(shù)據(jù)庫(kù)分庫(kù)分表的路由規(guī)則和負(fù)載均衡策略展開。

第 2 章 基本原理和概念

2.1 基本原理:

人類認(rèn)知問題的過程總是這樣的:what(什么)-?why(為什么)-?how(怎么
做),接下來,本文將就這三個(gè)問題展開討論和研究:

2.1.1 什么是數(shù)據(jù)切分

Shard 這個(gè)詞英文的意思是 碎片,而作為數(shù)據(jù)庫(kù)相關(guān)的技術(shù)用語(yǔ),似乎最早見于大型多人在線角色扮演游戲中。Sharding 姑且稱之為 分片。Sharding 不是一門新技術(shù),而是一個(gè)相對(duì)簡(jiǎn)樸的軟件理念。眾所周知,MySQL 5 之后才有了數(shù)據(jù)表分區(qū)功能,那么在此之前,很多 MySQL 的潛在用戶都對(duì) MySQL 的擴(kuò)展性有所顧慮,而是否具備分區(qū)功能就成了衡量一個(gè)數(shù)據(jù)庫(kù)可擴(kuò)展性與否的一個(gè)關(guān)鍵指標(biāo) (當(dāng)然不是唯一指標(biāo))。數(shù)據(jù)庫(kù)擴(kuò)展性是一個(gè)永恒的話題,MySQL 的推廣者經(jīng)常會(huì)被問到:如在單一數(shù)據(jù)庫(kù)上處理應(yīng)用數(shù)據(jù)捉襟見肘而需要進(jìn)行分區(qū)化之類的處理,是如何辦到的呢? 答案是:Sharding。  Sharding 不是一個(gè)某個(gè)特定數(shù)據(jù)庫(kù)軟件附屬的功能,而是在具體技術(shù)細(xì)節(jié)之上的抽象處理,是水平擴(kuò)展(Scale Out,亦或橫向擴(kuò)展、向外擴(kuò)展) 的解決方案,其主要目的是為突破單節(jié)點(diǎn)數(shù)據(jù)庫(kù)服務(wù)器的 I/O 能力限制,解決數(shù)據(jù)庫(kù)擴(kuò)展性問題。
通過一系列的切分規(guī)則將數(shù)據(jù)水平分布到不同的 DB 或 table 中,在通過相應(yīng)的 DB 路由或者 table 路由規(guī)則找到需要查詢的具體的 DB 或者 table,以進(jìn)行 Query 操作。這里所說的“sharding”通常是指“水平切分”,這也是本文討論的重點(diǎn)。具體將有什么樣的切分方式呢和路由方式呢?行文至此,讀者難免有所疑問,接下來舉個(gè)簡(jiǎn)單的例子:我們針對(duì)一個(gè) Blog 應(yīng)用中的日志來說明,比如日志文章(article)表有如下字段: 

 

面對(duì)這樣的一個(gè)表,我們?cè)鯓忧蟹帜兀吭鯓訉⑦@樣的數(shù)據(jù)分布到不同的數(shù)據(jù)庫(kù)中的表中去呢?其實(shí)分析 blog 的應(yīng)用,我們不難得出這樣的結(jié)論:blog 的應(yīng)用中,用戶分為兩種:瀏覽者和 blog 的主人。瀏覽者瀏覽某個(gè) blog,實(shí)際上是在一個(gè)特定的用戶的 blog 下進(jìn)行瀏覽的,而 blog 的主人管理自己的 blog,也同樣是在特定的用戶 blog 下進(jìn)行操作的(在自己的空間下)。所謂的特定的用戶,用數(shù)據(jù)庫(kù)的字段表示就是“user_id”。就是這個(gè)“user_id”,它就是我們需要的分庫(kù)的依據(jù)和規(guī)則的基礎(chǔ)。我們可以這樣做,將 user_id 為 1~10000 的所有的文章信息放入 DB1 中的 article 表中,將 user_id 為 10001~20000 的所有文章信息放入 DB2 中的 article 表中,以此類推,一直到 DBn。這樣一來,文章數(shù)據(jù)就很自然的被分到了各個(gè)數(shù)據(jù)庫(kù)中,達(dá)到了數(shù)據(jù)切分的目的。接下來要解決的問題就是怎樣找到具體的數(shù)據(jù)庫(kù)呢?其實(shí)問題也是簡(jiǎn)單明顯的,既然分庫(kù)的時(shí)候我們用到了區(qū)分字段 user_id,那么很自然,數(shù)據(jù)庫(kù)路由的過程當(dāng)然還是少不了 user_id 的。考慮一下我們剛才呈現(xiàn)的 blog 應(yīng)用,不管是訪問別人的 blog 還是管理自己的 blog,總之我都要知道這個(gè) blog 的用戶是誰吧,也就是我們知道了這個(gè) blog 的 user_id,就利用這個(gè) user_id,利用分庫(kù)時(shí)候的規(guī)則,反過來定位具體的數(shù)據(jù)庫(kù),比如 user_id 是 234,利用該才的規(guī)則,就應(yīng)該定位到 DB1,假如 user_id 是 12343,利用該才的規(guī)則,就應(yīng)該定位到 DB2。以此類推,利用分庫(kù)的規(guī)則,反向的路由到具體的 DB,這個(gè)過程我們稱之為“DB 路由”。
當(dāng)然考慮到數(shù)據(jù)切分的 DB 設(shè)計(jì)必然是非常規(guī),不正統(tǒng)的 DB 設(shè)計(jì)。那么什么樣的 DB 設(shè)計(jì)是正統(tǒng)的 DB 設(shè)計(jì)呢?
我們平常規(guī)規(guī)矩矩用的基本都是。平常我們會(huì)自覺的按照范式來設(shè)計(jì)我們的數(shù)據(jù)庫(kù),負(fù)載高點(diǎn)可能考慮使用相關(guān)的 Replication 機(jī)制來提高讀寫的吞吐和性能,這可能已經(jīng)可以滿足很多需求,但這套機(jī)制自身的缺陷還是比較顯而易見的(下文會(huì)提及)。上面提到的“自覺的按照范式設(shè)計(jì)”。考慮到數(shù)據(jù)切分的 DB 設(shè)計(jì),將違背這個(gè)通常的規(guī)矩和約束,為了切分,我們不得不在數(shù)據(jù)庫(kù)的表中出現(xiàn)冗余字段,用作區(qū)分字段或者叫做分庫(kù)的標(biāo)記字段,比如上面的 article 的例子中的 user_id 這樣的字段(當(dāng)然,剛才的例子并沒有很好的體現(xiàn)出 user_id 的冗余性,因?yàn)?user_id 這個(gè)字段即使就是不分庫(kù),也是要出現(xiàn)的,算是我們撿了便宜吧)。當(dāng)然冗余字段的出現(xiàn)并不只是在分庫(kù)的場(chǎng)景下才出現(xiàn)的,在很多大型應(yīng)用中,冗余也是必須的,這個(gè)涉及到高效 DB 的設(shè)計(jì),本文不再贅述。

2.1.2 為什么要數(shù)據(jù)切分

上面對(duì)什么是數(shù)據(jù)切分做了個(gè)概要的描述和解釋,讀者可能會(huì)疑問,為什么需要數(shù)據(jù)切分呢?像 Oracle 這樣成熟穩(wěn)定的數(shù)據(jù)庫(kù),足以支撐海量數(shù)據(jù)的存儲(chǔ)與查詢了?為什么還需要數(shù)據(jù)切片呢?的確,Oracle 的 DB 確實(shí)很成熟很穩(wěn)定,但是高昂的使用費(fèi)用和高端的硬件支撐不是每一個(gè)公司能支付的起的。試想一下一年幾千萬的使用費(fèi)用和動(dòng)輒上千萬元的小型機(jī)作為硬件支撐,這是一般公司能支付的起的嗎?即使就是能支付的起,假如有更好的方案,有更廉價(jià)且水平擴(kuò)展性能更好的方案,我們?yōu)槭裁床贿x擇呢?
但是,事情總是不盡人意。平常我們會(huì)自覺的按照范式來設(shè)計(jì)我們的數(shù)據(jù)庫(kù),負(fù)載高點(diǎn)可能考慮使用相關(guān)的 Replication 機(jī)制來提高讀寫的吞吐和性能,這可能已經(jīng)可以滿足很多需求,但這套機(jī)制自身的缺陷還是比較顯而易見的。首先它的有效很依賴于讀操作的比例,Master 往往會(huì)成為瓶頸所在,寫操作需要順序排隊(duì)來執(zhí)行,過載的話 Master 首先扛不住,Slaves 的數(shù)據(jù)同步的延遲也可能比較大,而且會(huì)大大耗費(fèi) CPU 的計(jì)算能力,因?yàn)?write 操作在 Master 上執(zhí)行以后還是需要在每臺(tái) slave 機(jī)器上都跑一次。這時(shí)候 Sharding 可能會(huì)成為雞肋了。Replication 搞不定,那么為什么 Sharding 可以工作呢?道理很簡(jiǎn)單,因?yàn)樗梢院芎玫臄U(kuò)展。我們知道每臺(tái)機(jī)器無論配置多么好它都有自身的物理上限,所以當(dāng)我們應(yīng)用已經(jīng)能觸及或遠(yuǎn)遠(yuǎn)超出單臺(tái)機(jī)器的某個(gè)上限的時(shí)候,我們惟有尋找別的機(jī)器的幫助或者繼續(xù)升級(jí)的我們的硬件,但常見的方案還是橫向擴(kuò)展, 通過添加更多的機(jī)器來共同承擔(dān)壓力。我們還得考慮當(dāng)我們的業(yè)務(wù)邏輯不斷增長(zhǎng),我們的機(jī)器能不能通過線性增長(zhǎng)就能滿足需求?Sharding 可以輕松的將計(jì)算,存儲(chǔ),I/ O 并行分發(fā)到多臺(tái)機(jī)器上,這樣可以充分利用多臺(tái)機(jī)器各種處理能力,同時(shí)可以避免單點(diǎn)失敗,提供系統(tǒng)的可用性,進(jìn)行很好的錯(cuò)誤隔離。
綜合以上因素,數(shù)據(jù)切分是很有必要的,且我們?cè)诖擞懻摰臄?shù)據(jù)切分也是將 MySql 作為背景的。基于成本的考慮,很多公司也選擇了 Free 且 Open 的 MySql。對(duì) MySql 有所了解的開發(fā)人員可能會(huì)知道,MySQL 5 之后才有了數(shù)據(jù)表分區(qū)功能,那么在此之前,很多 MySQL 的潛在用戶都對(duì) MySQL 的擴(kuò)展性有所顧慮,而是否具備分區(qū)功能就成了衡量一個(gè)數(shù)據(jù)庫(kù)可擴(kuò)展性與否的一個(gè)關(guān)鍵指標(biāo)(當(dāng)然不是唯一指標(biāo))。數(shù)據(jù)庫(kù)擴(kuò)展性是一個(gè)永恒的話題,MySQL 的推廣者經(jīng)常會(huì)被問到:如在單一數(shù)據(jù)庫(kù)上處理應(yīng)用數(shù)據(jù)捉襟見肘而需要進(jìn)行分區(qū)化之類的處理,是如何辦到的呢? 答案也是 Sharding,也就是我們所說的數(shù)據(jù)切分方案。
  我們用免費(fèi)的 MySQL 和廉價(jià)的 Server 甚至是 PC 做集群,達(dá)到小型機(jī) + 大型商業(yè) DB 的效果,減少大量的資金投入,降低運(yùn)營(yíng)成本,何樂而不為呢?所以,我們選擇 Sharding,擁抱 Sharding。

2.1.3 怎么做到數(shù)據(jù)切分

說到數(shù)據(jù)切分,再次我們講對(duì)數(shù)據(jù)切分的方法和形式進(jìn)行比較詳細(xì)的闡述和說明。
數(shù)據(jù)切分可以是物理上的,對(duì)數(shù)據(jù)通過一系列的切分規(guī)則將數(shù)據(jù)分布到不同的 DB 服務(wù)器上,通過路由規(guī)則路由訪問特定的數(shù)據(jù)庫(kù),這樣一來每次訪問面對(duì)的就不是單臺(tái)服務(wù)器了,而是 N 臺(tái)服務(wù)器,這樣就可以降低單臺(tái)機(jī)器的負(fù)載壓力。
數(shù)據(jù)切分也可以是數(shù)據(jù)庫(kù)內(nèi)的,對(duì)數(shù)據(jù)通過一系列的切分規(guī)則,將數(shù)據(jù)分布到一個(gè)數(shù)據(jù)庫(kù)的不同表中,比如將 article 分為 article_001,article_002 等子表,若干個(gè)子表水平拼合有組成了邏輯上一個(gè)完整的 article 表,這樣做的目的其實(shí)也是很簡(jiǎn)單的。舉個(gè)例子說明,比如 article 表中現(xiàn)在有 5000w 條數(shù)據(jù),此時(shí)我們需要在這個(gè)表中增加(insert)一條新的數(shù)據(jù),insert 完畢后,數(shù)據(jù)庫(kù)會(huì)針對(duì)這張表重新建立索引,5000w 行數(shù)據(jù)建立索引的系統(tǒng)開銷還是不容忽視的。但是反過來,假如我們將這個(gè)表分成 100 個(gè) table 呢,從 article_001 一直到 article_100,5000w 行數(shù)據(jù)平均下來,每個(gè)子表里邊就只有 50 萬行數(shù)據(jù),這時(shí)候我們向一張只有 50w 行數(shù)據(jù)的 table 中 insert 數(shù)據(jù)后建立索引的時(shí)間就會(huì)呈數(shù)量級(jí)的下降,極大了提高了 DB 的運(yùn)行時(shí)效率,提高了 DB 的并發(fā)量。當(dāng)然分表的好處還不知這些,還有諸如寫操作的鎖操作等,都會(huì)帶來很多顯然的好處。
綜上,分庫(kù)降低了單點(diǎn)機(jī)器的負(fù)載;分表,提高了數(shù)據(jù)操作的效率,尤其是 Write 操作的效率。行文至此我們依然沒有涉及到如何切分的問題。接下來,我們將對(duì)切分規(guī)則進(jìn)行詳盡的闡述和說明。
上文中提到,要想做到數(shù)據(jù)的水平切分,在每一個(gè)表中都要有相冗余字符作為切分依據(jù)和標(biāo)記字段,通常的應(yīng)用中我們選用 user_id 作為區(qū)分字段,基于此就有如下三種分庫(kù)的方式和規(guī)則:(當(dāng)然還可以有其他的方式)
按號(hào)段分:
(1) user_id 為區(qū)分,1~1000 的對(duì)應(yīng) DB1,1001~2000 的對(duì)應(yīng) DB2,以此類推;
優(yōu)點(diǎn):可部分遷移
缺點(diǎn):數(shù)據(jù)分布不均

(2)hash 取模分:
對(duì) user_id 進(jìn)行 hash(或者如果 user_id 是數(shù)值型的話直接使用 user_id 的值也可),然后用一個(gè)特定的數(shù)字,比如應(yīng)用中需要將一個(gè)數(shù)據(jù)庫(kù)切分成 4 個(gè)數(shù)據(jù)庫(kù)的話,我們就用 4 這個(gè)數(shù)字對(duì) user_id 的 hash 值進(jìn)行取模運(yùn)算,也就是 user_id%4, 這樣的話每次運(yùn)算就有四種可能:結(jié)果為 1 的時(shí)候?qū)?yīng) DB1;結(jié)果為 2 的時(shí)候?qū)?yīng) DB2;結(jié)果為 3 的時(shí)候?qū)?yīng) DB3;結(jié)果為 0 的時(shí)候?qū)?yīng) DB4,這樣一來就非常均勻的將數(shù)據(jù)分配到 4 個(gè) DB 中。
優(yōu)點(diǎn):數(shù)據(jù)分布均勻
缺點(diǎn):數(shù)據(jù)遷移的時(shí)候麻煩,不能按照機(jī)器性能分?jǐn)倲?shù)據(jù)
(3)在認(rèn)證庫(kù)中保存數(shù)據(jù)庫(kù)配置
就是建立一個(gè) DB,這個(gè) DB 單獨(dú)保存 user_id 到 DB 的映射關(guān)系,每次訪問數(shù)據(jù)庫(kù)的時(shí)候都要先查詢一次這個(gè)數(shù)據(jù)庫(kù),以得到具體的 DB 信息,然后才能進(jìn)行我們需要的查詢操作。
優(yōu)點(diǎn):靈活性強(qiáng),一對(duì)一關(guān)系
缺點(diǎn):每次查詢之前都要多一次查詢,性能大打折扣
以上就是通常的開發(fā)中我們選擇的三種方式,有些復(fù)雜的項(xiàng)目中可能會(huì)混合使用這三種方式。通過上面的描述,我們對(duì)分庫(kù)的規(guī)則也有了簡(jiǎn)單的認(rèn)識(shí)和了解。當(dāng)然還會(huì)有更好更完善的分庫(kù)方式,還需要我們不斷的探索和發(fā)現(xiàn)。

第 3 章 本課題研究的基本輪廓

上面的文字,我們按照人類認(rèn)知事物的規(guī)律,what?why?how 這樣的方式闡述了數(shù)據(jù)庫(kù)切分的一些概念和意義以及對(duì)一些常規(guī)的切分規(guī)則做了概要的介紹。本課題所討論的分布數(shù)據(jù)層并不僅僅如此,它是一個(gè)完整的數(shù)據(jù)層解決方案,它到底是什么樣的呢?接下來的文字,我將詳細(xì)闡述本研究課題的完整思想和實(shí)現(xiàn)方式。
分布式數(shù)據(jù)方案提供功能如下:
(1)提供分庫(kù)規(guī)則和路由規(guī)則(RouteRule 簡(jiǎn)稱 RR),將上面的說明中提到的三中切分規(guī)則直接內(nèi)嵌入本系統(tǒng),具體的嵌入方式在接下來的內(nèi)容中進(jìn)行詳細(xì)的說明和論述;
(2)引入集群(Group)的概念,保證數(shù)據(jù)的高可用性;
(3)引入負(fù)載均衡策略(LoadBalancePolicy 簡(jiǎn)稱 LB);
(4)引入集群節(jié)點(diǎn)可用性探測(cè)機(jī)制,對(duì)單點(diǎn)機(jī)器的可用性進(jìn)行定時(shí)的偵測(cè),以保證 LB 策略的正確實(shí)施,以確保系統(tǒng)的高度穩(wěn)定性;
(5)引入讀 / 寫分離,提高數(shù)據(jù)的查詢速度;
僅僅是分庫(kù)分表的數(shù)據(jù)層設(shè)計(jì)也是不夠完善的,當(dāng)某個(gè)節(jié)點(diǎn)上的 DB 服務(wù)器出現(xiàn)了宕機(jī)的情況的時(shí)候,會(huì)是什么樣的呢?是的,我們采用了數(shù)據(jù)庫(kù)切分方案,也就是說有 N 太機(jī)器組成了一個(gè)完整的 DB,如果有一臺(tái)機(jī)器宕機(jī)的話,也僅僅是一個(gè) DB 的 N 分之一的數(shù)據(jù)不能訪問而已,這是我們能接受的,起碼比切分之前的情況好很多了,總不至于整個(gè) DB 都不能訪問。一般的應(yīng)用中,這樣的機(jī)器故障導(dǎo)致的數(shù)據(jù)無法訪問是可以接受的,假設(shè)我們的系統(tǒng)是一個(gè)高并發(fā)的電子商務(wù)網(wǎng)站呢?單節(jié)點(diǎn)機(jī)器宕機(jī)帶來的經(jīng)濟(jì)損失是非常嚴(yán)重的。也就是說,現(xiàn)在我們這樣的方案還是存在問題的,容錯(cuò)性能是經(jīng)不起考驗(yàn)的。當(dāng)然了,問題總是有解決方案的。我們引入集群的概念,在此我稱之為 Group,也就是每一個(gè)分庫(kù)的節(jié)點(diǎn)我們引入多臺(tái)機(jī)器,每臺(tái)機(jī)器保存的數(shù)據(jù)是一樣的,一般情況下這多臺(tái)機(jī)器分?jǐn)傌?fù)載,當(dāng)出現(xiàn)宕機(jī)情況,負(fù)載均衡器將分配負(fù)載給這臺(tái)宕機(jī)的機(jī)器。這樣一來,
就解決了容錯(cuò)性的問題。所以我們引入了集群的概念,并將其內(nèi)嵌入我們的框架中,成為框架的一部分。 
 

如上圖所示,整個(gè)數(shù)據(jù)層有 Group1,Group2,Group3 三個(gè)集群組成,這三個(gè)集群就是數(shù)據(jù)水平切分的結(jié)果,當(dāng)然這三個(gè)集群也就組成了一個(gè)包含完整數(shù)據(jù)的 DB。每一個(gè) Group 包括 1 個(gè) Master(當(dāng)然 Master 也可以是多個(gè))和 N 個(gè) Slave,這些 Master 和 Slave 的數(shù)據(jù)是一致的。比如 Group1 中的一個(gè) slave 發(fā)生了宕機(jī)現(xiàn)象,那么還有兩個(gè) slave 是可以用的,這樣的模型總是不會(huì)造成某部分?jǐn)?shù)據(jù)不能訪問的問題,除非整個(gè) Group 里的機(jī)器全部宕掉,但是考慮到這樣的事情發(fā)生的概率非常小(除非是斷電了,否則不易發(fā)生吧)。
在沒有引入集群以前,我們的一次查詢的過程大致如下:請(qǐng)求數(shù)據(jù)層,并傳遞必要的分庫(kù)區(qū)分字段(通常情況下是 user_id)? 數(shù)據(jù)層根據(jù)區(qū)分字段 Route 到具體的 DB? 在這個(gè)確定的 DB 內(nèi)進(jìn)行數(shù)據(jù)操作。這是沒有引入集群的情況,當(dāng)時(shí)引入集群會(huì)是什么樣子的呢?看圖一即可得知,我們的路由器上規(guī)則和策略其實(shí)只能路由到具體的 Group,也就是只能路由到一個(gè)虛擬的 Group,這個(gè) Group 并不是某個(gè)特定的物理服務(wù)器。接下來需要做的工作就是找到具體的物理的 DB 服務(wù)器,以進(jìn)行具體的數(shù)據(jù)操作。基于這個(gè)環(huán)節(jié)的需求,我們引入了負(fù)載均衡器的概念(LB)。負(fù)載均衡器的職責(zé)就是定位到一臺(tái)具體的 DB 服務(wù)器。具體的規(guī)則如下:負(fù)載均衡器會(huì)分析當(dāng)前 sql 的讀寫特性,如果是寫操作或者是要求實(shí)時(shí)性很強(qiáng)的操作的話,直接將查詢負(fù)載分到 Master,如果是讀操作則通過負(fù)載均衡策略分配一個(gè) Slave。我們的負(fù)載均衡器的主要研究放向也就是負(fù)載分發(fā)策略,通常情況下負(fù)載均衡包括隨機(jī)負(fù)載均衡和加權(quán)負(fù)載均衡。隨機(jī)負(fù)載均衡很好理解,就是從 N 個(gè) Slave 中隨機(jī)選取一個(gè) Slave。這樣的隨機(jī)負(fù)載均衡是不考慮機(jī)器性能的,它默認(rèn)為每臺(tái)機(jī)器的性能是一樣的。假如真實(shí)的情況是這樣的,這樣做也是無可厚非的。假如實(shí)際情況并非如此呢?每個(gè) Slave 的機(jī)器物理性能和配置不一樣的情況,再使用隨機(jī)的不考慮性能的負(fù)載均衡,是非常不科學(xué)的,這樣一來會(huì)給機(jī)器性能差的機(jī)器帶來不必要的高負(fù)載,甚至帶來宕機(jī)的危險(xiǎn),同時(shí)高性能的數(shù)據(jù)庫(kù)服務(wù)器也不能充分發(fā)揮其物理性能。基于此考慮從,我們引入了加權(quán)負(fù)載均衡,也就是在我們的系統(tǒng)內(nèi)部通過一定的接口,可以給每臺(tái) DB 服務(wù)器分配一個(gè)權(quán)值,然后再運(yùn)行時(shí) LB 根據(jù)權(quán)值在集群中的比重,分配一定比例的負(fù)載給該 DB 服務(wù)器。當(dāng)然這樣的概念的引入,無疑增大了系統(tǒng)的復(fù)雜性和可維護(hù)性。有得必有失,我們也沒有辦法逃過的。
有了分庫(kù),有了集群,有了負(fù)載均衡器,是不是就萬事大吉了呢?事情遠(yuǎn)沒有我們想象的那么簡(jiǎn)單。雖然有了這些東西,基本上能保證我們的數(shù)據(jù)層可以承受很大的壓力,但是這樣的設(shè)計(jì)并不能完全規(guī)避數(shù)據(jù)庫(kù)宕機(jī)的危害。假如 Group1 中的 slave2 宕機(jī)了,那么系統(tǒng)的 LB 并不能得知,這樣的話其實(shí)是很危險(xiǎn)的,因?yàn)?LB 不知道,它還會(huì)以為 slave2 為可用狀態(tài),所以還是會(huì)給 slave2 分配負(fù)載。這樣一來,問題就出來了,客戶端很自然的就會(huì)發(fā)生數(shù)據(jù)操作失敗的錯(cuò)誤或者異常。這樣是非常不友好的!怎樣解決這樣的問題呢?我們引入集群節(jié)點(diǎn)的可用性探測(cè)機(jī)制,或者是可用性的數(shù)據(jù)推送機(jī)制。這兩種機(jī)制有什么不同呢?首先說探測(cè)機(jī)制吧,顧名思義,探測(cè)即使,就是我的數(shù)據(jù)層客戶端,不定時(shí)對(duì)集群中各個(gè)數(shù)據(jù)庫(kù)進(jìn)行可用性的嘗試,實(shí)現(xiàn)原理就是嘗試性鏈接,或者數(shù)據(jù)庫(kù)端口的嘗試性訪問,都可以做到,當(dāng)然也可以用 JDBC 嘗試性鏈接,利用 Java 的 Exception 機(jī)制進(jìn)行可用性的判斷,具體的會(huì)在后面的文字中提到。那數(shù)據(jù)推送機(jī)制又是什么呢?其實(shí)這個(gè)就要放在現(xiàn)實(shí)的應(yīng)用場(chǎng)景中來討論這個(gè)問題了,一般情況下應(yīng)用的 DB 數(shù)據(jù)庫(kù)宕機(jī)的話我相信 DBA 肯定是知道的,這個(gè)時(shí)候 DBA 手動(dòng)的將數(shù)據(jù)庫(kù)的當(dāng)前狀態(tài)通過程序的方式推送到客戶端,也就是分布式數(shù)據(jù)層的應(yīng)用端,這個(gè)時(shí)候在更新一個(gè)本地的 DB 狀態(tài)的列表。并告知 LB,這個(gè)數(shù)據(jù)庫(kù)節(jié)點(diǎn)不能使用,請(qǐng)不要給它分配負(fù)載。一個(gè)是主動(dòng)的監(jiān)聽機(jī)制,一個(gè)是被動(dòng)的被告知的機(jī)制。兩者各有所長(zhǎng)。但是都可以達(dá)到同樣的效果。這樣一來剛才假設(shè)的問題就不會(huì)發(fā)生了,即使就是發(fā)生了,那么發(fā)生的概率也會(huì)降到最低。
上面的文字中提到的 Master 和 Slave,我們并沒有做太多深入的講解。如圖一所示,一個(gè) Group 由 1 個(gè) Master 和 N 個(gè) Slave 組成。為什么這么做呢?其中 Master 負(fù)責(zé)寫操作的負(fù)載,也就是說一切寫的操作都在 Master 上進(jìn)行,而讀的操作則分?jǐn)偟?Slave 上進(jìn)行。這樣一來的可以大大提高讀取的效率。在一般的互聯(lián)網(wǎng)應(yīng)用中,經(jīng)過一些數(shù)據(jù)調(diào)查得出結(jié)論,讀 / 寫的比例大概在 10:1 左右,也就是說大量的數(shù)據(jù)操作是集中在讀的操作,這也就是為什么我們會(huì)有多個(gè) Slave 的原因。但是為什么要分離讀和寫呢?熟悉 DB 的研發(fā)人員都知道,寫操作涉及到鎖的問題,不管是行鎖還是表鎖還是塊鎖,都是比較降低系統(tǒng)執(zhí)行效率的事情。我們這樣的分離是把寫操作集中在一個(gè)節(jié)點(diǎn)上,而讀操作其其他的 N 個(gè)節(jié)點(diǎn)上進(jìn)行,從另一個(gè)方面有效的提高了讀的效率,保證了系統(tǒng)的高可用性。讀寫分離也會(huì)引入新的問題,比如我的 Master 上的數(shù)據(jù)怎樣和集群中其他的 Slave 機(jī)器保持?jǐn)?shù)據(jù)的同步和一致呢? 這個(gè)是我們不需要過多的關(guān)注的問題,MySql 的 Proxy 機(jī)制可以幫助我們做到這點(diǎn),由于 Proxy 機(jī)制與本課題相關(guān)性不是太強(qiáng),
在這里不做詳細(xì)介紹。
綜上所述,本課題中所研究的分布式數(shù)據(jù)層的大體功能就是如此。以上是對(duì)基本原理的一些討論和闡述。接下來就系統(tǒng)設(shè)計(jì)層面,進(jìn)行深入的剖析和研究。

第 4 章 系統(tǒng)設(shè)計(jì)

4.1 系統(tǒng)實(shí)現(xiàn)層面的選擇

在引言部分中提到,該系統(tǒng)的實(shí)現(xiàn)層面有兩種選擇,一種是基于 JDBC 層面上的選擇,一種是基于現(xiàn)有數(shù)據(jù)持久層框架層面上的選擇,比如 Hibernate,ibatis。兩種層面各有長(zhǎng)處,也各有不足之處。基于 JDBC 層面上的系統(tǒng)實(shí)現(xiàn),系統(tǒng)開發(fā)難度和后期的使用難度都將大大提高。大大增加了系統(tǒng)的開發(fā)費(fèi)用和維護(hù)費(fèi)用。本課題的定位是在成型的 ibatis 持久層框架的基礎(chǔ)上進(jìn)行上層的封裝,而不是對(duì) ibatis 源碼的直接修改,這樣一來使本系統(tǒng)不會(huì)對(duì)現(xiàn)有框架有太多的侵入性,從而也增加了使用的靈活性。之所以選擇 ibatis,原因如下:
(1)ibatis 的學(xué)習(xí)成本非常低,熟練的 Java Programmer 可在非常的短時(shí)間內(nèi)熟練使用 ibatis;
(2)ibatis 是輕量級(jí)的 ORM,只是簡(jiǎn)單的完成了 RO,OR 的映射,其查詢語(yǔ)句也是通過配置文件 sql-map.xml 文件在原生 sql 的層面進(jìn)行簡(jiǎn)單的配置,也就是說我們沒有引入諸如 Hibernate 那樣的 HQL 的概念,從而增強(qiáng)了 sql 的可控性,優(yōu)秀的 DBA 可以很好的從 sql 的層面對(duì) sql 進(jìn)行優(yōu)化,使數(shù)據(jù)層的應(yīng)用有很強(qiáng)的可控性。Hibernate 雖然很強(qiáng)大,但是由于 Hibernate 是 OR 的一個(gè)重型封裝,且引入 HQL 的概念,不便于 DBA 團(tuán)隊(duì)對(duì) sql 語(yǔ)句的控制和性能的調(diào)優(yōu)。
基于以上兩點(diǎn)理由,本課題在 ORM 的產(chǎn)品的選擇上選擇了易學(xué)易用且輕量級(jí)的持久層框架 ibatis。下面的討論也都是特定于 ibatis 的基礎(chǔ)上的討論。

4.2 其他開源框架的選擇

在一些大型的 Java 應(yīng)用中,我們通常會(huì)采用 Spring 這樣的開源框架,尤其是 IoC(DI)這部分,有效的幫助開發(fā)人員管理對(duì)象的依賴關(guān)系和層次,降低系統(tǒng)各層次之間的實(shí)體耦合。Spring 的優(yōu)點(diǎn)和用處我相信這是開發(fā)人員眾所周知的,在此不再贅述。本課題的數(shù)據(jù)層也將采用 Spring 做為 IoC(DI)的框架。
4.3 系統(tǒng)開發(fā)技術(shù)和工具介紹
開發(fā)語(yǔ)言:Java JDK1.5
集成開發(fā)環(huán)境:Eclipse 3.3.4
Web 環(huán)境下測(cè)試服務(wù)器:JBoss 4.2
構(gòu)建工具:淘寶自行研發(fā)的構(gòu)建工具 Antx(類似于 Maven),當(dāng)然也可以用 Maven
依賴的開源 Jar:Spring2.0,ibaits,commons-configuration(讀取配置文件),log4j,junit 等

第 5 章 系統(tǒng)分析

1、系統(tǒng)有那些關(guān)鍵數(shù)據(jù)需要切分
系統(tǒng)有靜態(tài)數(shù)據(jù)和動(dòng)態(tài)數(shù)據(jù)。
靜態(tài)數(shù)據(jù)往往數(shù)據(jù)量小不需要切分,那么就需要獨(dú)立一個(gè)單獨(dú)的靜態(tài)配置庫(kù),考慮到很多 sql 一定會(huì)用 join 的方式進(jìn)行關(guān)聯(lián)靜態(tài)數(shù)據(jù),那么靜態(tài)數(shù)據(jù)肯定會(huì)往動(dòng)態(tài)數(shù)據(jù)庫(kù)中進(jìn)行復(fù)制。同時(shí),一個(gè)單獨(dú)的靜態(tài)庫(kù),對(duì)于靜態(tài)數(shù)據(jù)的管理非常方便。
動(dòng)態(tài)數(shù)據(jù),是只有橫向切分,還是采用縱向切分加上橫向切分。比如:按照用戶和 blog 進(jìn)行縱向切分后,在進(jìn)行用戶和 blog 的橫向切分。
切分的粒度需要精確掌握,否則會(huì)引起開發(fā)和維護(hù)的很多困難。
2、全局唯一 ID
如果采用 hash 分庫(kù)或者地理位置屬性分庫(kù)進(jìn)行了橫向切分后,用戶 ID 或者 blog 的 ID 肯定具有含義,能夠通過 ID 定位到對(duì)應(yīng)的切分庫(kù)中。
無論那種方式,ID 一定是有含義的,從字面上或者算法上都能夠識(shí)別數(shù)據(jù)歸屬那個(gè)切分庫(kù)。
3、切分?jǐn)?shù)據(jù)一體化(連帶性)
如果是一個(gè)龐大的數(shù)據(jù)庫(kù),其中必定有很多模型。比如:用戶模型,資料模型等等,是否一個(gè)用戶對(duì)應(yīng)的資料都能夠在這個(gè)用戶對(duì)應(yīng)的數(shù)據(jù)庫(kù)中找到。這樣做,會(huì)盡量減少開發(fā)的難度,不可能完全的要求開發(fā)避免關(guān)聯(lián)表的使用。
4、分表的管理
采用了橫向數(shù)據(jù)庫(kù)切分后,必然存在數(shù)據(jù)庫(kù)分表。
是否切分庫(kù)中存在一個(gè)分表,還是一個(gè)切分庫(kù)中存在多個(gè)分表。
那些分表在那個(gè)庫(kù)中,那些分表歸屬一個(gè)切分庫(kù),這些都需要管理。
如果一個(gè)切分庫(kù)就一個(gè)類型的一張分表,也太浪費(fèi)數(shù)據(jù)庫(kù)資源了。
5、分庫(kù)后的連接管理
數(shù)據(jù)庫(kù)切分后,數(shù)據(jù)庫(kù)增多,必然會(huì)導(dǎo)致連接的增多。
一個(gè)應(yīng)用是連接所有的分庫(kù)?還是劃分某個(gè)功能的應(yīng)用連接特定的分庫(kù)?
如果采用 oracle 的 rac,還要考慮 rac 的 gc buffer busy 的問題。
6、開發(fā)上 sql 的配合
如果系統(tǒng)不大,可以采用配置 sql+xml 文件方式。采用分庫(kù)的系統(tǒng),一般都很龐大。
采用 sql+xml 的配置,一定會(huì)出現(xiàn)很多問題,開發(fā)的難度和不配合程度一定會(huì)直線上升。需要將 sql 下放給開發(fā)人員,針對(duì) sql 中的表可以采用抽象和邏輯的管理模式。既提高了開發(fā)效率,又能管理需要執(zhí)行到分庫(kù)中的 sql。
7、業(yè)務(wù)參數(shù)
如果業(yè)務(wù)邏輯很復(fù)雜,要求每一個(gè)函數(shù)或者每一個(gè)方法,都顯示的傳遞分庫(kù)參數(shù)。就將分庫(kù)的邏輯和應(yīng)用完全的柔和在一起,將導(dǎo)致后期的維護(hù)和升級(jí)極其的困難。
采用在頁(yè)面或者接口層面?zhèn)鬟f一次業(yè)務(wù)邏輯參數(shù),后期在 orm 底層使用這些參數(shù)。
8、業(yè)務(wù)的取舍
以前的應(yīng)用,可以按照名字進(jìn)行查詢。分庫(kù)后,無法進(jìn)行這樣的查詢,需要按照分庫(kù)屬性進(jìn)行查詢。將會(huì)導(dǎo)致業(yè)務(wù)上或者上操作上的變更。
一般會(huì)有些業(yè)務(wù)的操作會(huì)隨著分庫(kù)的進(jìn)行而變動(dòng)。
9、分布式事務(wù)
切分多個(gè)庫(kù)后,難免有業(yè)務(wù)操作多個(gè)數(shù)據(jù)庫(kù),那么就會(huì)帶來分布式事務(wù)的問題(分布式事務(wù)不僅僅出現(xiàn)在多個(gè)數(shù)據(jù)庫(kù)中)。同時(shí),一個(gè)繁忙的數(shù)據(jù)庫(kù),進(jìn)行跨庫(kù)操作的時(shí)候,要避免跨庫(kù)的死鎖。數(shù)據(jù)庫(kù)可不會(huì)檢測(cè)到另外一個(gè)庫(kù)與自己的死鎖。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注丸趣 TV 行業(yè)資訊頻道,感謝您對(duì)丸趣 TV 的支持。

正文完
 
丸趣
版權(quán)聲明:本站原創(chuàng)文章,由 丸趣 2023-08-03發(fā)表,共計(jì)10436字。
轉(zhuǎn)載說明:除特殊說明外本站除技術(shù)相關(guān)以外文章皆由網(wǎng)絡(luò)搜集發(fā)布,轉(zhuǎn)載請(qǐng)注明出處。
評(píng)論(沒有評(píng)論)
主站蜘蛛池模板: 平阳县| 汉寿县| 兰考县| 潮州市| 沈丘县| 涟水县| 怀安县| 信宜市| 望城县| 宁津县| 包头市| 运城市| 富裕县| 东兴市| 固原市| 邵东县| 九江县| 施甸县| 临颍县| 吉林省| 许昌县| 英吉沙县| 高唐县| 湟中县| 漠河县| 郎溪县| 东明县| 彰武县| 伊宁县| 边坝县| 梨树县| 日喀则市| 台北县| 博野县| 满城县| 昆明市| 梅州市| 满洲里市| 台北县| 沁阳市| 阿巴嘎旗|