架構(gòu)設(shè)計(jì):系統(tǒng)存儲(chǔ)MySQL橫向拆分與業(yè)務(wù)透明化_第1頁
架構(gòu)設(shè)計(jì):系統(tǒng)存儲(chǔ)MySQL橫向拆分與業(yè)務(wù)透明化_第2頁
架構(gòu)設(shè)計(jì):系統(tǒng)存儲(chǔ)MySQL橫向拆分與業(yè)務(wù)透明化_第3頁
架構(gòu)設(shè)計(jì):系統(tǒng)存儲(chǔ)MySQL橫向拆分與業(yè)務(wù)透明化_第4頁
架構(gòu)設(shè)計(jì):系統(tǒng)存儲(chǔ)MySQL橫向拆分與業(yè)務(wù)透明化_第5頁
已閱讀5頁,還剩14頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、架構(gòu)設(shè)計(jì):系統(tǒng)存儲(chǔ)MySQL橫向拆分與業(yè)務(wù)透明化使用MyCat配置橫向拆分之前文章中我們介紹了如何使用MyCat進(jìn)行讀寫分離,類似的關(guān)系型數(shù)據(jù)庫的讀寫分離存儲(chǔ)方案可以在保持上層業(yè)務(wù)系統(tǒng)透明度的基礎(chǔ)上滿足70%業(yè)務(wù)系統(tǒng)的數(shù)據(jù)承載規(guī)模要求和性能要求。比起單純使用LVS + Replicaion的讀寫分離方案而言最大的優(yōu)勢(shì)在于更能增加對(duì)上層業(yè)務(wù)系統(tǒng)的透明性。當(dāng)然如果 您覺得單個(gè)MyCat節(jié)點(diǎn)在高可用范疇或者性能范疇上還需要增強(qiáng),還可以使用Keepalived、LVS等組件在多個(gè)MyCat節(jié)點(diǎn)上組成高可用集群或者負(fù)載集群。但是這個(gè)方案也有一個(gè)明顯的問題,那就是它沒有解決數(shù)據(jù)存儲(chǔ)規(guī)模的瓶頸。如果單個(gè)節(jié)點(diǎn)

2、上某個(gè)單表的數(shù)據(jù)規(guī)模超過了千萬級(jí),那么這個(gè)節(jié)點(diǎn)的讀操作也會(huì)產(chǎn)生性能瓶頸。所以我們還需要進(jìn)一步使用MyCat的分片技術(shù)對(duì)業(yè)務(wù)數(shù)據(jù)表進(jìn)行橫向拆分。要說清楚MyCat對(duì)橫向拆分的支持,就首先要說清楚關(guān)系型數(shù)據(jù)庫橫向拆分所面臨的主要問題,以及MyCat為了解決這些問題所作的努力。總的來說橫向拆分的所面臨的問題主要分為兩大類,一類是數(shù)據(jù)讀的問題一類是數(shù)據(jù)寫的問題。本節(jié)我們首先分析討論一下數(shù)據(jù)讀的問題,后文中介紹MyCat對(duì)分布式事務(wù)的支持時(shí)我們?cè)賮碛懻摍M向拆分時(shí)的數(shù)據(jù)寫問題。4-3-1、數(shù)據(jù)分片中的數(shù)據(jù)讀操作問題select TableA.*,TableB.xname,TableC.xcode from

3、 TableAleft join TableB on TableB.id = TableA.b_codeidleft join TableC on TableC.a_id = TableA.idwhere TableA.groupname = 'XXXX'以上查詢語句是我們?cè)跇I(yè)務(wù)系統(tǒng)數(shù)據(jù)查詢的過程中經(jīng)常使用的一種查詢類型,是一種多個(gè)數(shù)據(jù)表進(jìn)行左外連接的查詢語句。其中TableA業(yè)務(wù)表擁有大量的數(shù)據(jù)且變化頻率非常高,是需要進(jìn)行拆分的主要數(shù)據(jù)表;TableB業(yè)務(wù)表可能是一張字典表,雖然它有比較大的數(shù)據(jù),但遠(yuǎn)遠(yuǎn)沒有達(dá)到千萬級(jí)別并且變化頻率很低(每天最多有10000次數(shù)據(jù)寫操作);Ta

4、bleC業(yè)務(wù)表中的數(shù)據(jù)量也很大,從技術(shù)角度上說該業(yè)務(wù)表可以做拆分也可以不做拆分,其中的TableC.a_id字段和TableA.id字段也是一種弱關(guān)聯(lián),也就是說TableC中的業(yè)務(wù)數(shù)據(jù)就算沒有關(guān)聯(lián)TableA中的業(yè)務(wù)數(shù)據(jù)也可以相對(duì)獨(dú)立的工作。當(dāng)然以上說的是一種可能的業(yè)務(wù)數(shù)據(jù)狀態(tài),實(shí)際情況還可能更復(fù)雜。如果這些業(yè)務(wù)表同在一個(gè)數(shù)據(jù)庫中,甚至是存在于同一個(gè)MySQL實(shí)例的不同數(shù)據(jù)庫中,那么執(zhí)行以上查詢語句基本上沒有什么難度,技術(shù)人員使用MySQL的執(zhí)行計(jì)劃也可以很清晰的看到查詢語句的執(zhí)行過程:但是如果在分庫狀態(tài)下,那么查詢過程就沒有這么簡單了。首先來說,主要需要進(jìn)行數(shù)據(jù)拆分處理的TableA中的數(shù)據(jù)

5、分布在不同的數(shù)據(jù)庫中,這些數(shù)據(jù)庫工作在不同的MySQL實(shí)例上。另外業(yè)務(wù)表TableC中的數(shù)據(jù)也進(jìn)行了拆分,但是拆分時(shí)并沒有參考和其可能有關(guān)聯(lián)的TableA中的業(yè)務(wù)數(shù)據(jù)存儲(chǔ)分片情況,也就是說原來已有的關(guān)聯(lián)在拆分存儲(chǔ)后可能就消失了,而且即使拆分后的數(shù)據(jù)關(guān)聯(lián)還存在,但拆分前和拆分后執(zhí)行數(shù)據(jù)排序操作的結(jié)構(gòu)也可能是不同的。至于字典表TableB,由于可預(yù)見的時(shí)間內(nèi)數(shù)據(jù)總規(guī)模不大,所以可以不進(jìn)行拆分所有拆分后的數(shù)據(jù)庫中TableB數(shù)據(jù)表的數(shù)據(jù)內(nèi)容完全一樣。下圖展示了一種數(shù)據(jù)表中數(shù)據(jù)進(jìn)行隨機(jī)拆分后可能的存儲(chǔ)結(jié)構(gòu)和產(chǎn)生的問題:這樣來看,數(shù)據(jù)表橫向拆分過程中至少需要考慮以下讀操作問題:橫向拆分后數(shù)據(jù)表之間的邏輯

6、關(guān)聯(lián)問題:數(shù)據(jù)表間存在各種關(guān)聯(lián),有的關(guān)聯(lián)甚至還存在外鍵約束。數(shù)據(jù)拆分后的關(guān)聯(lián)關(guān)系應(yīng)該和拆分前的關(guān)聯(lián)關(guān)系保持一致,至少應(yīng)該保證通過數(shù)據(jù)庫中間件查詢得到的數(shù)據(jù)關(guān)聯(lián)結(jié)果和拆分前的關(guān)聯(lián)結(jié)果保持一致。橫向拆分后數(shù)據(jù)的排序和分頁問題:由于數(shù)據(jù)拆分后,排序動(dòng)作會(huì)分別在各個(gè)拆分后的數(shù)據(jù)庫中單獨(dú)執(zhí)行,這可能就會(huì)導(dǎo)致拆分后的排序和分頁結(jié)果和拆分前的結(jié)果不一致。那么數(shù)據(jù)庫中間件需要保證能夠?qū)⑦@些結(jié)果集合進(jìn)行整合并還原成拆分前的排序和分頁結(jié)果。橫向拆分后數(shù)據(jù)的分組操作問題:分組和統(tǒng)計(jì)操作同樣存在和以上描述類似的問題,各個(gè)拆分后的數(shù)據(jù)庫將單獨(dú)執(zhí)行分組和統(tǒng)計(jì),這就可能導(dǎo)致用一個(gè)分區(qū)條件在各個(gè)拆分?jǐn)?shù)據(jù)庫中都有分組和統(tǒng)計(jì)結(jié)果

7、。數(shù)據(jù)庫中間件還是需要保證能夠合并這些分組統(tǒng)計(jì)結(jié)果,并保證它們和拆分前的數(shù)據(jù)庫操作結(jié)果一致。4-3-2、全局表在數(shù)據(jù)庫的橫向拆分過程中,各種數(shù)據(jù)字典表基本上不需要進(jìn)行拆分。這是因?yàn)檫@些數(shù)據(jù)表的數(shù)據(jù)規(guī)模都不會(huì)太大且變化頻率較低,另外一個(gè)原因是減少橫向拆分后表關(guān)聯(lián)操作的難度。類似省市縣信息、手機(jī)區(qū)號(hào)信息、功能菜單信息等數(shù)據(jù)都應(yīng)算作字典數(shù)據(jù)。根據(jù)實(shí)際工作經(jīng)驗(yàn),并不會(huì)出現(xiàn)所有業(yè)務(wù)表的數(shù)據(jù)規(guī)模都達(dá)到或超過千萬級(jí)規(guī)模,只有部分關(guān)鍵業(yè)務(wù)表的數(shù)據(jù)規(guī)模會(huì)出現(xiàn)這樣的情況,基于這樣的情況只有這些業(yè)務(wù)表和與它們直接關(guān)聯(lián)的部分?jǐn)?shù)據(jù)表需要進(jìn)行數(shù)據(jù)拆分設(shè)計(jì)。MyCat數(shù)據(jù)庫中間件中為除了以上情況外,各個(gè)不需要進(jìn)行數(shù)據(jù)拆分的

8、數(shù)據(jù)表提供了一種冗余復(fù)制方案:全局表。如果一張業(yè)務(wù)表在schema.xml配置文件配設(shè)置成了全局表,那么MyCat將在涉及這張業(yè)務(wù)表的所有分片節(jié)點(diǎn)上保持這張數(shù)居表中數(shù)據(jù)完全一致。Mycat在Join操作中,業(yè)務(wù)表與全局表進(jìn)行Join聚合會(huì)優(yōu)先選擇相同分片內(nèi)的全局表join,避免跨庫 Join;在進(jìn)行數(shù)據(jù)插入操作時(shí),MyCat將把數(shù)據(jù)分發(fā)到全局表對(duì)應(yīng)的所有分片執(zhí)行,在進(jìn)行數(shù)據(jù)讀取時(shí)候?qū)?huì)隨機(jī)獲取一個(gè)節(jié)點(diǎn)讀取數(shù)據(jù)。schema.xml配置文件中的全局表配置類似如下:.<schema .> # 請(qǐng)注意這里的type屬性,屬性值為“global”,代表全局表 # 這樣,在dn1和dn2兩個(gè)

9、分片節(jié)點(diǎn)中的t_area業(yè)務(wù)表中,其數(shù)據(jù)將保持完全一致。 <table name="t_area" primaryKey="id" type="global" dataNode="dn1,dn2" /></schema>.分片表為了在表關(guān)聯(lián)查詢性能和表關(guān)聯(lián)處理難易程度之間取得平衡,MyCat提供了兩種分片表類型和多種分片規(guī)則。對(duì)于業(yè)務(wù)關(guān)聯(lián)較為獨(dú)立的需要進(jìn)行數(shù)據(jù)分片的業(yè)務(wù)表可以采用普通分片。然而有一類情況是,需要進(jìn)行數(shù)據(jù)分片的業(yè)務(wù)表有一些非常重要的關(guān)聯(lián)數(shù)據(jù)也同時(shí)需要進(jìn)行分片,例如訂單(orde

10、r)數(shù)據(jù)表和訂單明細(xì)(order_detail)數(shù)據(jù)表。很明顯訂單數(shù)據(jù)和訂單明細(xì)數(shù)據(jù)是經(jīng)常需要進(jìn)行關(guān)聯(lián)查詢的,并且既然訂單數(shù)據(jù)達(dá)到了一定的規(guī)模需要進(jìn)行數(shù)據(jù)分片,那么只會(huì)比它數(shù)據(jù)量更大的訂單明細(xì)表也同時(shí)需要進(jìn)行分片。在這樣的關(guān)聯(lián)分片情況下,MyCat需要保證訂單明細(xì)A1、A2、A3、A4數(shù)據(jù)能夠正確的寫入到他們關(guān)聯(lián)的訂單信息A所在的分片上。這樣才能保證訂單A在join查詢訂單明細(xì)時(shí),向請(qǐng)求者返回正確的查詢結(jié)果。MyCat提供的這種分片模式稱為ER分片/智能分片。在后續(xù)4-4、4-5和4-6節(jié)中,本文將和讀者一起來討論MyCat中支持?jǐn)?shù)據(jù)分片的兩種關(guān)鍵分片表類型,普通分片和智能分片。我們還會(huì)一起討

11、論MyCat中主要支持的數(shù)據(jù)分片規(guī)則,包括mod-long、partbymonth、rang-mod、rang-long、hash-int等分片規(guī)則。MyCat還支持開發(fā)人員自定義分片規(guī)則,這個(gè)自定義方式也會(huì)進(jìn)行介紹。4-3-4、Share join和catlet(人工智能)MyCat還向技術(shù)人員提供了兩種不同分片的查詢匯總功能,其中Share join是一個(gè)簡單的跨分片Join方式,目前支持 2 個(gè)表的 join,原理就是解析 SQL 語句,拆分成單表的 SQL 語句執(zhí)行,然后把各個(gè)節(jié)點(diǎn)的數(shù)據(jù)匯 集;另外一種catlet人工智能分片查詢功能,是將Join查詢語句分析后,從指定分片提取查詢結(jié)果

12、的前半部分,然后將查詢結(jié)果送入其它分片以便可以結(jié)合到這個(gè)結(jié)果所關(guān)聯(lián)的其他數(shù)據(jù)。Share join查詢的做法和人工智能分片查詢的做法往往無法實(shí)現(xiàn)高性能處理,所以這兩種不同分片的數(shù)據(jù)關(guān)聯(lián)查詢方式只適合開發(fā)人員使用,不建議在生產(chǎn)環(huán)境中使用。4-4、普通分片場景示例數(shù)據(jù)表普通分片是比較好理解的概念,即是說一個(gè)擁有相對(duì)獨(dú)立業(yè)務(wù)的數(shù)據(jù)表,按照一定的拆分規(guī)則將數(shù)據(jù)分別存儲(chǔ)在若干個(gè)獨(dú)立的數(shù)據(jù)庫中的操作。能夠進(jìn)行這種分片操作的數(shù)據(jù)表的特點(diǎn)是,業(yè)務(wù)耦合度一般較低或者屬于基礎(chǔ)性功能模塊;這種數(shù)據(jù)表也可能存在和其它數(shù)據(jù)的關(guān)聯(lián),但是關(guān)聯(lián)的是一個(gè)或者多個(gè)字典數(shù)據(jù)表;即使這種數(shù)據(jù)表存在直接關(guān)聯(lián)的其它業(yè)務(wù)數(shù)據(jù),那么后者的數(shù)

13、據(jù)規(guī)模和變化頻率也不會(huì)在可預(yù)見的時(shí)間內(nèi)進(jìn)行數(shù)據(jù)分片操作。這種場景在實(shí)際業(yè)務(wù)中是比較常見的,典型的就是用戶基礎(chǔ)信息:在產(chǎn)品第N次迭代時(shí),考慮了后續(xù)幾個(gè)月內(nèi)注冊(cè)用戶量將突破1000萬大關(guān),且半年內(nèi)將繼續(xù)成幾何級(jí)增長。這時(shí)架構(gòu)師就必須考慮對(duì)“用戶中心子系統(tǒng)”中用戶基本信息進(jìn)行分庫處理。用戶基本信息快速遷移/割接的問題很好解決,由于目前用戶基本信息只有百萬左右,所以可以考慮在每個(gè)分片庫先做整體冗余,然后再后續(xù)運(yùn)維工作中再進(jìn)行數(shù)據(jù)清掃。也可以在最初階段就考慮合適的分片規(guī)則,保證這幾百萬數(shù)據(jù)在后續(xù)存儲(chǔ)方案升級(jí)中將可以作為整個(gè)MySQL分庫分表集群的第一個(gè)分片節(jié)點(diǎn)組(后文在講解分片規(guī)則時(shí)會(huì)詳細(xì)講到)“用戶中

14、心子系統(tǒng)”中我們?yōu)榭赡艿?000萬用戶數(shù)據(jù)規(guī)模規(guī)劃了5個(gè)分片,每個(gè)分片中做兩組讀寫分離,每組讀寫分離包含一個(gè)寫節(jié)點(diǎn)和二至三個(gè)讀節(jié)點(diǎn)。并且這兩個(gè)組的寫節(jié)點(diǎn)互為主從。以下是schema.xml主配置文件中重要的設(shè)置內(nèi)容:<mycat:schema xmlns:mycat="http:/io.mycat/"> <!- 在這個(gè)測(cè)試示例中一共有三張邏輯表 -> <schema name="usercenterSchema" checkSQLschema="false" sqlMaxLimit="200&q

15、uot;> <!- 以下是若干張不需要進(jìn)行數(shù)據(jù)分片的字典性質(zhì)的數(shù)據(jù)表 它們都以全局表的形式在每個(gè)分片節(jié)點(diǎn)上擁有完全一致的數(shù)據(jù) -> <table name="dictionaryA" primaryKey="Id" type="global" dataNode="dn1,dn2,dn3,dn4,dn5" /> <table name="dictionaryB" primaryKey="Id" type="global"

16、dataNode="dn1,dn2,dn3,dn4,dn5" /> <table name="dictionaryC" primaryKey="Id" type="global" dataNode="dn1,dn2,dn3,dn4,dn5" /> <table name="dictionaryD" primaryKey="Id" type="global" dataNode="dn1,dn2,dn3,d

17、n4,dn5" /> <!- 這是在本示例中我們需要進(jìn)行分片的用戶基本信息數(shù)據(jù)表 -> <table name="usertable" primaryKey="Id" dataNode="dn1,dn2,dn3,dn4,dn5" rule="mod-long"/> </schema> <!- 設(shè)置五個(gè)邏輯節(jié)點(diǎn)/分片節(jié)點(diǎn) 這里注意一個(gè)問題,如果為了節(jié)約成本可以將某兩個(gè)或者某幾個(gè)邏輯節(jié)點(diǎn) 運(yùn)行在相同的MySQL物理機(jī)群上,那么建議database屬性取不同的名字

18、 例如stacks01、stacks02、stacks03. -> <dataNode name="dn1" dataHost="dataHost1" database="stacks" /> <dataNode name="dn2" dataHost="dataHost2" database="stacks" /> <dataNode name="dn3" dataHost="dataHost3"

19、database="stacks" /> <dataNode name="dn4" dataHost="dataHost4" database="stacks" /> <dataNode name="dn5" dataHost="dataHost5" database="stacks" /> <!- 第一個(gè)分片節(jié)點(diǎn)中定義了兩個(gè)寫操作節(jié)點(diǎn)和其對(duì)應(yīng)的讀操作節(jié)點(diǎn), writeType設(shè)置為0,表示一般情況下所有寫操作都發(fā)送到配

20、置的第一個(gè)寫節(jié)點(diǎn)上, 另一個(gè)寫節(jié)點(diǎn)和讀節(jié)點(diǎn)充當(dāng)standby的角色。 -> <dataHost name="dataHost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="2"> <heartbeat>select user()</heartbeat&

21、gt; <writeHost host="dataHost1_hostM1" url="40:3306" user="root" password="123456"> <readHost host="dataHost1_hostS11" url="41:3306" user="root" password="123456"/> <readHost host=&

22、quot;dataHost1_hostS12" url="42:3306" user="root" password="123456"/> </writeHost> <writeHost host="dataHost1_hostM2" url="50:3306" user="root" password="123456"> <readHost host="

23、;dataHost1_hostS21" url="51:3306" user="root" password="123456"/> <readHost host="dataHost1_hostS22" url="52:3306" user="root" password="123456"/> </writeHost> </dataHost> .</my

24、cat:schema>以上配置示例中關(guān)于分片規(guī)則的部分(table標(biāo)簽的rule屬性),我們將在后文中專門進(jìn)行介紹。讀者在這里只需要知道“mod-long”是一種長整形取余的分片方式就可以了,另外全局表和分片表唯一的設(shè)置差別就是全局表需要明確指定type屬性,而分片表不需要。很明顯至少從現(xiàn)在的情況看,用戶基本信息雖然有直接關(guān)聯(lián)的數(shù)據(jù)信息,但是關(guān)聯(lián)的都是字典性質(zhì)的數(shù)據(jù)表,這些數(shù)據(jù)表都被設(shè)置為全局表,所以在任何分片中用戶基本信息都可以找到與它正確關(guān)聯(lián)的信息。最后需要再次注意的MyCat并不負(fù)責(zé)數(shù)據(jù)同步過程,所以所有節(jié)點(diǎn)的數(shù)據(jù)同步還需要技術(shù)人員根據(jù)頂層設(shè)計(jì)自行配置。4-5、ER分片及使用限制4

25、-5-1、ER分片基本使用經(jīng)過上一節(jié)示例的技術(shù)迭代過程,為不久的將來線上業(yè)務(wù)系統(tǒng)達(dá)到5000萬級(jí)別用戶基本信息的數(shù)據(jù)存儲(chǔ)規(guī)模的準(zhǔn)備工作就完成了,但是新的要求又來了:我們需要記錄最近一年時(shí)間內(nèi)用戶對(duì)基本信息的修改情況。這部分修改情況可能來源于另一套日志采集系統(tǒng)(例如一套基于Apache Flume + Apache Kafka + Apache Storm的日志數(shù)據(jù)實(shí)時(shí)采集分析平臺(tái))也可能直接來源于業(yè)務(wù)系統(tǒng)對(duì)數(shù)據(jù)變化的判斷,這里我們并不討論數(shù)據(jù)的來源問題,而只討論這部分用戶基本信息變化數(shù)據(jù)的存儲(chǔ)情況假設(shè)技術(shù)團(tuán)隊(duì)已經(jīng)決定使用關(guān)系型數(shù)據(jù)庫存儲(chǔ)這些變化數(shù)據(jù)。很顯然用戶基本信息的修改明細(xì)和用戶基本信息存

26、在很強(qiáng)的關(guān)聯(lián)關(guān)系,且用戶基本信息的修改明細(xì)也需要進(jìn)行分片。當(dāng)用戶基本信息A進(jìn)入分片數(shù)據(jù)庫X時(shí),需要和這個(gè)用戶基本信息進(jìn)行關(guān)聯(lián)的修改明細(xì)信息也必須正確進(jìn)入數(shù)據(jù)庫X,這樣才能保持?jǐn)?shù)據(jù)關(guān)聯(lián)的正確性。這是因?yàn)椋喝绻麛?shù)據(jù)表存在外鍵約束設(shè)定,那么用戶信息修改明細(xì)錯(cuò)誤寫入分片時(shí)就會(huì)導(dǎo)致寫操作直接報(bào)錯(cuò)分片數(shù)據(jù)庫無法找到關(guān)聯(lián)的用戶信息。而使用外鍵約束又是明確被建議的數(shù)據(jù)庫設(shè)計(jì)方式。即使數(shù)據(jù)表不存在外鍵約束設(shè)定,雖然用戶信息修改明細(xì)可以寫入和用戶基本信息不一致的分片,但是在基于用戶基本信息進(jìn)行關(guān)聯(lián)查詢時(shí)就無法查詢到正確的關(guān)聯(lián)信息??磥硪诒3中阅艿那疤嵯陆鉀Q這個(gè)問題,就必須保證父級(jí)表和子級(jí)表在同時(shí)需要分片時(shí),相關(guān)

27、聯(lián)數(shù)據(jù)能夠正確寫入相同的分片中,MyCat稱這樣的分片表為ER分片表。MyCat的主配置文件中使用table標(biāo)簽的子標(biāo)簽childTable對(duì)ER分片表的關(guān)系進(jìn)行標(biāo)識(shí)。如下示例:.<table name="usertable" primaryKey="Id" dataNode="dn1,dn2" rule="mod-long"> <childTable name="booktable" primaryKey="Id" joinKey="author

28、id" parentKey="Id"/></table>.關(guān)于table標(biāo)簽已經(jīng)在上文中介紹過了,這里的使用方式相似。需要注意的是childTable標(biāo)簽的幾個(gè)關(guān)鍵屬性:primaryKey屬性:該屬性和table標(biāo)簽中的primaryKey屬性意義相同,表示該邏輯表對(duì)應(yīng)真實(shí)表的主鍵。joinKey屬性和parentKey屬性:在進(jìn)行childTable表數(shù)據(jù)插入時(shí),MyCat會(huì)首先依據(jù)joinKey屬性設(shè)置的字段拿到本次數(shù)據(jù)插入時(shí)該字段的值,然后再根據(jù)parentKey屬性指定的父級(jí)Table的列信息生成查詢語句,以便確定將要插入的這條數(shù)據(jù),其

29、父級(jí)數(shù)據(jù)在哪個(gè)分片上。有的讀者可能就要提問了:為什么不采用已設(shè)置的分片規(guī)則重新計(jì)算出數(shù)據(jù)存放的分片呢?這是因?yàn)榉制?guī)則可能會(huì)產(chǎn)生變化,即使分片規(guī)則沒有產(chǎn)生變化,很多規(guī)則下作為計(jì)算基準(zhǔn)的“可用分片數(shù)量”也可能產(chǎn)生了變化。了解了ER分片表的基本工作方式,我們就可以對(duì)上一節(jié)用戶中心使用的普通分片場景進(jìn)行調(diào)整,在其為用戶基本信息修改明細(xì)配置ER分片關(guān)系,調(diào)整后的配置文件如下所示(只列出了關(guān)鍵的變化位置,其他全局表的設(shè)置沒有變化):.<!- 原來的全局表設(shè)置還是沒有變 -><table name="dictionaryD" primaryKey="Id&q

30、uot; type="global" dataNode="dn1,dn2,dn3,dn4,dn5" /><!- 這是在本示例中我們需要進(jìn)行分片的用戶基本信息數(shù)據(jù)表-><table name="usertable" primaryKey="Id" dataNode="dn1,dn2,dn3,dn4,dn5" rule="mod-long"> <!- 用戶基本信息修改明細(xì) -> <childTable name="user

31、modifyDetails" primaryKey="Id" joinKey="userid" parentKey="Id"/></table>.請(qǐng)注意,我們并沒有為childTable設(shè)置分片規(guī)則和可以使用的分片節(jié)點(diǎn),這是因?yàn)閏hildTable每一條數(shù)據(jù)存儲(chǔ)的位置是由它父級(jí)Table表中每一條數(shù)據(jù)的實(shí)際存儲(chǔ)位置決定。通過ER分片我們可以保證類似如下的join關(guān)聯(lián)語句能夠在每個(gè)分片中正確執(zhí)行,并被匯總到MyCat服務(wù)上。這是MyCat服務(wù)對(duì)這些分片結(jié)果進(jìn)行正確的二次整合的前提條件。# 無論是這兩張數(shù)據(jù)表做

32、怎樣的join關(guān)聯(lián),都可以保證沒個(gè)分片中的查詢結(jié)果是正確的。# 如以下這種查詢方法select usertable.*,usermodifyDetails.fieldname,usermodifyDetails.fieldnewValue from usertableleft join usermodifyDetails on usertable.Id = usermodifyDetails.userid# 或者這種查詢方法,又或者其它的只涉及這兩個(gè)數(shù)據(jù)表的一對(duì)多、多對(duì)一關(guān)聯(lián)查詢select usermodifyDetails.*,usertable.Id,usertable.username

33、 from usermodifyDetailsleft join usertable on usertable.Id = usermodifyDetails.userid主要分片規(guī)則上文提到MyCat的邏輯表支持多種分片規(guī)則,表現(xiàn)于schema配置文件中中table標(biāo)簽的rule屬性。本節(jié)將以MyCat Version 1.6版為基礎(chǔ),介紹幾種經(jīng)常使用的分片規(guī)則,這些分片規(guī)則都通過rule.xml文件進(jìn)行定義和配置。4-6-1、分片枚舉sharding-by-intfile.<tableRule name="sharding-by-intfile"> <r

34、ule> <!- columns表示分片計(jì)算時(shí)的取值列,記得設(shè)置成您的數(shù)據(jù)表列名 -> <columns>sharding_id</columns> <algorithm>hash-int</algorithm> </rule></tableRule>.<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap"> <property name="

35、mapFile">partition-hash-int.txt</property></function>.實(shí)現(xiàn)類io.mycat.route.function.PartitionByFileMap,這個(gè)分片規(guī)則是直接按照partition-hash-int.txt文件(默認(rèn))中定義的固定分片規(guī)則進(jìn)行分片。其中可以設(shè)置的屬性包括:type:這個(gè)分片規(guī)則也不支持按照字符串為分片依據(jù),當(dāng)type屬性的值為0時(shí)表示Integer,非零表示String,默認(rèn)的屬性值為0。defaultNode:從MyCat Version 1.6版本開始,rule.xml配置文

36、件中就不再設(shè)置默認(rèn)分片節(jié)點(diǎn)了。但是這個(gè)屬性還是存在的,如果碰到不識(shí)別的枚舉值,就讓它路由到默認(rèn)節(jié)點(diǎn);如果不配置默認(rèn)節(jié)點(diǎn)(defaultNode值小于0表示不配置默認(rèn)節(jié)點(diǎn)),碰到不識(shí)別的枚舉值就會(huì)報(bào)錯(cuò):like this:cant find datanode for sharding column:column_name val:ffffffff。mapFile:固定枚舉分片所設(shè)置的枚舉規(guī)則存儲(chǔ)的文件,默認(rèn)的文件名就是partition-hash-int.txt,并且和rule.xml文件在一個(gè)工作目錄下。partition-hash-int.txt文件的定義類似如下:# 如果取值列的值為100

37、00則將數(shù)據(jù)送入0號(hào)分片10000=010010=1我們來跟蹤一下PartitionByFileMap類的源碼,會(huì)發(fā)現(xiàn)這個(gè)分片規(guī)則工作很簡單簡單有它的好處,不會(huì)在分片過程中浪費(fèi)太多的計(jì)算時(shí)間計(jì)算分片目標(biāo):.public class PartitionByFileMap extends AbstractPartionAlgorithm implements RuleAlgorithm . / 默認(rèn)節(jié)點(diǎn)在map中的key private static final String DEFAULT_NODE = "DEFAULT_NODE" / 在配置文件的配置規(guī)則會(huì)被初始化到app

38、2Partition對(duì)象中 / 這個(gè)Map對(duì)象的value表示分片節(jié)點(diǎn)編號(hào)從0開始編號(hào) private Map<Object, Integer> app2Partition; . public Integer calculate(String columnValue) / columnValue既是當(dāng)前將要進(jìn)行的數(shù)據(jù)分片所取的列值 Object value = columnValue; / 如果配置文件設(shè)置了type為0,則需要將值轉(zhuǎn)為數(shù)字 if(type = 0) value = Integer.valueOf(columnValue); Integer rst = null;

39、/ 取得關(guān)聯(lián)的分片,從app2Partition對(duì)象中 / 注意,這個(gè)地方的變量命名可能會(huì)使閱讀者產(chǎn)生歧義 / 實(shí)際上這value對(duì)象代表了app2Partition中的key信息。 Integer pid = app2Partition.get(value); if (pid != null) rst = pid; / 如果沒有取到對(duì)應(yīng)的分片,則試圖取默認(rèn)分片 else rst =app2Partition.get(DEFAULT_NODE); return rst; .4-6-2、取模的分片方式:mod-long.<tableRule name="mod-long"

40、;> <rule> <!- columns表示分片計(jì)算時(shí)的取值列,記得設(shè)置成您的數(shù)據(jù)表列名 -> <columns>id</columns> <algorithm>mod-long</algorithm> </rule></tableRule>.<function name="mod-long" class="io.mycat.route.function.PartitionByMod"> <!- how many ata nodes

41、-> <property name="count">2</property></function>.實(shí)現(xiàn)類:io.mycat.route.function.PartitionByMod,這個(gè)分片規(guī)則只適合數(shù)字形式的列,并且規(guī)則的實(shí)現(xiàn)更簡單:既是按照將要插入數(shù)據(jù)的指定列進(jìn)行取模運(yùn)算。該規(guī)則需要設(shè)置一個(gè)count屬性,這個(gè)數(shù)據(jù)就是現(xiàn)存的分片節(jié)點(diǎn)的個(gè)數(shù)用于進(jìn)行取模運(yùn)算的基數(shù):.public class PartionByMod extends AbstractPartionAlgorithm implements RuleAlgorith

42、m . # 該屬性設(shè)置當(dāng)前的分片節(jié)點(diǎn)數(shù)量 private int count; . public Integer calculate(String columnValue) # 取絕對(duì)值 BigInteger bigNum = new BigInteger(columnValue).abs(); # 返回取模運(yùn)算的值 # 如果您不清楚BigInteger類型的特性,請(qǐng)自行查閱資料 return (bigNum.mod(BigInteger.valueOf(count).intValue(); .4-6-3、約定數(shù)字范圍auto-sharding-long.<tableRule name=

43、"auto-sharding-long"> <rule> <!- columns表示分片計(jì)算時(shí)的取值列,記得設(shè)置成您的數(shù)據(jù)表列名 -> <columns>id</columns> <algorithm>rang-long</algorithm> </rule></tableRule>.<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong&

44、quot;> <property name="mapFile">autopartition-long.txt</property></function>.實(shí)現(xiàn)類:io.mycat.route.function.AutoPartitionByLong,這個(gè)規(guī)則下MyCat會(huì)使用autopartition-long.txt文件(默認(rèn)的文件路徑)中已經(jīng)設(shè)置好的數(shù)字范圍決定將數(shù)據(jù)存儲(chǔ)到哪個(gè)分片中,autopartition-long.txt文件中的配置類似如下:# 這個(gè)配置文件的含義是# 指定分片列的數(shù)值為05000000的數(shù)據(jù)都在0號(hào)分片

45、中0-500M=0# 指定分片列的數(shù)值為500000010000000的數(shù)據(jù)都在1號(hào)分片中500M-1000M=11000M-1500M=2如果您的業(yè)務(wù)系統(tǒng)后續(xù)考慮方便的增加分片節(jié)點(diǎn),可以考慮優(yōu)先使用這種分片規(guī)則,因?yàn)檫@種固定設(shè)置的分片規(guī)則既簡單又實(shí)用。以下是AutoPartitionByLong類的重要判斷代碼,通過查看代碼中主要的分片過程,有助于我們理解這種分片規(guī)則:.public class AutoPartitionByLong extends AbstractPartionAlgorithm implements RuleAlgorithm private String mapFil

46、e; / defaultNode設(shè)置為-1,表示沒有默認(rèn)的分片節(jié)點(diǎn)。 private int defaultNode = -1; . / longRongs在初始化時(shí)被設(shè)置 / 在autopartition-long.txt文件中有幾行設(shè)置范圍,就有幾個(gè)LongRange對(duì)象。(參見該類中沒有列出的initialize方法) private LongRange longRongs; . public Integer calculate(String columnValue) long value = Long.valueOf(columnValue); Integer rst = null;

47、for (LongRange longRang : this.longRongs) / 如果條件成立,就說明找到了可以承載columnValue值的那個(gè)數(shù)據(jù)庫分片編號(hào) if (value <= longRang.valueEnd && value >= longRang.valueStart) return longRang.nodeIndx; /數(shù)據(jù)超過范圍,暫時(shí)使用配置的默認(rèn)節(jié)點(diǎn)(如果有的話) if(rst =null && defaultNode >= 0) return defaultNode; / 返回null,則說明沒有找到可承載c

48、olumnValue的分片編號(hào) / 也沒有進(jìn)行默認(rèn)分片的設(shè)置(這是MyCat會(huì)報(bào)錯(cuò)) return rst; . / LongRange對(duì)象有三個(gè)屬性: static class LongRange / 該屬性為當(dāng)前范圍所存儲(chǔ)的分片編號(hào) public final int nodeIndx; / 該屬性為范圍的開始值 public final long valueStart; / 該屬性為范圍的結(jié)束值 public final long valueEnd; . .注意,在以上代碼片段的注釋中有一句“暫時(shí)使用配置的默認(rèn)節(jié)點(diǎn)”。這句話不是筆者的注釋,而是該類的編寫者“wuzhi”加的注釋,也就是說這

49、里判斷成立后的處理代碼可能會(huì)在后續(xù)版本中進(jìn)行修改。4-6-4、自然月分片:sharding-by-month.<tableRule name="sharding-by-month"> <rule> <!- columns屬性同樣表示分片計(jì)算時(shí)的取值列,記得設(shè)置成您的數(shù)據(jù)表列名 -> <columns>create_date</columns> <algorithm>partbymonth</algorithm> </rule></tableRule>.<func

50、tion name="partbymonth" class="io.mycat.route.function.PartitionByMonth"> <property name="dateFormat">yyyy-MM-dd</property> <property name="sBeginDate">2015-01-01</property></function>.實(shí)現(xiàn)類io.mycat.route.function.PartitionByMont

51、h,該分片規(guī)則可按照每個(gè)自然月為一個(gè)分片數(shù)據(jù)庫,也就是說技術(shù)團(tuán)隊(duì)可以將數(shù)據(jù)放置到分片數(shù)據(jù)庫中,并且每個(gè)月增加一個(gè)分片數(shù)據(jù)庫,而不對(duì)之前的分片數(shù)據(jù)庫產(chǎn)生影響。該規(guī)則有三個(gè)屬性可以設(shè)置:dateFormat:日期數(shù)據(jù)列的數(shù)據(jù)格式。該屬性必須要設(shè)置,否則分區(qū)規(guī)則將無法進(jìn)行正常工作。默認(rèn)的配置文件中,這個(gè)數(shù)據(jù)都存在默認(rèn)值,就是以上示例中看到的yyyy-MM-dd。sBeginDate:這個(gè)屬性是指存儲(chǔ)的數(shù)據(jù)中,涉及的分區(qū)日期列的開始時(shí)間。sEndDate:這個(gè)屬性是指存儲(chǔ)的數(shù)據(jù)中,涉及的分區(qū)日期列的結(jié)束時(shí)間,該值可以不進(jìn)行設(shè)置。實(shí)際上設(shè)置sEndDate和不設(shè)置sEndDate,PartitionBy

52、Month將按照兩種不同的方式工作,詳見下文代碼分析。.public class PartitionByMonth extends AbstractPartitionAlgorithm implements RuleAlgorithm . / 以下三個(gè)屬性就是技術(shù)人員在配置文件中設(shè)置的屬性 private String sBeginDate; private String dateFormat; private String sEndDate; / 日期計(jì)算對(duì)象,專門用于根據(jù)設(shè)置開始事件和結(jié)束時(shí)間參與分片計(jì)算 private Calendar beginDate; private Calend

53、ar endDate; / 這個(gè)參數(shù)很重要,分區(qū)總數(shù),后面的代碼分析中會(huì)進(jìn)行講述 private int nPartition; . public void init() . / 關(guān)鍵點(diǎn)在這里,當(dāng)技術(shù)人員設(shè)置了sEndDate的值后 / 該規(guī)則將計(jì)算一個(gè)nPartition的參數(shù)值 if(sEndDate!=null&&!sEndDate.equals("") endDate = Calendar.getInstance(); endDate.setTime(new SimpleDateFormat(dateFormat).parse(sEndDate);

54、/ nPartition 為結(jié)束時(shí)間和開始時(shí)間的年差 * 12 / 加上結(jié)束時(shí)間和開始時(shí)間的月差 最后再 + 1 / 如果結(jié)束時(shí)間為2016-05,開始時(shí)間為2015-08則: / 12 + 7 - 4 + 1 = 16個(gè)月 nPartition = (endDate.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR) * 12 + endDate.get(Calendar.MONTH) - beginDate.get(Calendar.MONTH) + 1; / nPartition出現(xiàn)小于0的情況,則說明開始時(shí)間和結(jié)束時(shí)間的設(shè)置錯(cuò)誤。 if

55、(nPartition <= 0) throw new java.lang.IllegalArgumentException("Incorrect time range for month partitioning!"); / 如果沒有設(shè)置結(jié)束時(shí)間,則nPartition為-1。 else nPartition = -1; . / 正式的分片計(jì)算 public Integer calculate(String columnValue) try int targetPartition; Calendar curTime = Calendar.getInstance();

56、 curTime.setTime(formatter.get().parse(columnValue); / 目標(biāo)分片的計(jì)算方式為: / 當(dāng)前時(shí)間和開始時(shí)間的年差 * 12 / 加上當(dāng)前時(shí)間和開始時(shí)間的月差 / 如果開始時(shí)間為2015-08,當(dāng)前時(shí)間為2015-12則: / 0 + 11 - 7 = 4,也就是第五個(gè)分片庫 targetPartition = (curTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR) * 12 + curTime.get(Calendar.MONTH) - beginDate.get(Calendar.

57、MONTH); / 如果設(shè)置了結(jié)束時(shí)間,說明分片數(shù)量是有限制的,需要在reCalculatePartition方法中,基于targetPartition進(jìn)行取余運(yùn)算 if (nPartition > 0) targetPartition = reCalculatePartition(targetPartition); return targetPartition; catch (ParseException e) throw new IllegalArgumentException(new StringBuilder().append("columnValue:").append(columnValue).append(" Please chec

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論