免费高清特黄a大片,九一h片在线免费看,a免费国产一级特黄aa大,国产精品国产主播在线观看,成人精品一区久久久久,一级特黄aa大片,俄罗斯无遮挡一级毛片

分享

筆者帶你剖析輕量級Sharding中間件——Kratos1.x

 CevenCheng 2015-08-25

《筆者帶你剖析輕量級Sharding中間件——Kratos1.x》

 

之所以編寫Kratos其實存在一個小插曲,當筆者滿山遍野尋找成熟、穩(wěn)定、高性能的Sharding中間件時,確實是翻山越嶺,只不過始終沒有找到一款合適筆者項目場景的中間件產(chǎn)品。依稀記得當年第一款使用的Sharding中間件就是淘寶的TDDL3.0,只可惜現(xiàn)在拿不到源碼。而其它的中間件,大多都是基于Proxy的,相信做過分布式系統(tǒng)的人都知道,拋開網(wǎng)絡消耗所帶來的性能問題不談,多一個外圍系統(tǒng)依賴就意味著需要多增加和承擔一份風險,因此與應用集成的方式,則成為筆者選擇的首要條件。當然不是說基于Proxy方式的Sharding中間件不好,只能說我個人并不看重通用型需求,僅此而已。其實目前社區(qū)非?;钴S的MyCat,筆者在這里要批評一下,既然開源,那么就請在GitHub上貼上使用手冊,而不是配置手冊,因為對于一個任何一個DVP而言,他要的是迅速上手的幫助文檔,而不是要使用你們的開源產(chǎn)品還需要在淘寶上購買幾十塊一本的網(wǎng)絡書,對于這一點,我非常鄙視和厭惡。

 

許多開發(fā)人員動不動就大談分庫分表來顯示自己的成就感,那么筆者需要做的事情就是將其拉下神壇,讓大家切切實實感受到一款平民化技術所帶來的親民性。作為一款數(shù)據(jù)路由工具,Kratos采用與應用集成的方式為開發(fā)人員帶來超強的易用性。就目前而言,筆者測試環(huán)境上,物理環(huán)境選用為32庫和1024表的庫內分片模式,因此筆者就先不必王婆賣瓜,自賣自夸其所謂的高性能和高穩(wěn)定。至于你是否能夠從Kratos中獲益,取決于你是否想用最簡單,最易上手的方式來解決分庫分表場景下的數(shù)據(jù)路由工作。

 

筆者此篇博文,并不是宣導和普及分庫分表理論,而是作為Kratos貨真價實的Sharding權威指南手冊呈現(xiàn)給大家。不過實在抱歉,目前Kratos暫無開源打算,只提供免費試用,如果后期有開源計劃,會在第一時間發(fā)布在GitHub上。

 

目錄

一、Kratos簡介;

二、互聯(lián)網(wǎng)當下的數(shù)據(jù)拆分過程;

三、目前市面上常見的一些Sharding中間件產(chǎn)品對比;

四、Kratos的架構原型;

五、動態(tài)數(shù)據(jù)源層的Master/Slave讀寫分離;

六、Kratos的Sharding模型;

七、Sharding之庫內分片;

八、Sharding之一庫一片;

九、自動生成全局唯一的sequenceId;

十、自動生成kratos分庫分表配置文件;

十一、注意事項;

 

一、Kratos簡介

因為找不到合適的Shading中間件,其次一些開源的Shading中間件動不動就幾萬行的代碼真有必要嗎?因此誕生了編寫自己中間件的想發(fā)。Kratos這個名字來源于筆者之前在PS3平臺玩的一款ACT游戲《戰(zhàn)神3》中嗜血殺神的主角奎爺Kratos,盡管筆者的Kratos并沒有展現(xiàn)出秒殺其他Sharding中間件的霸氣,但Kratos要做的事情很純粹,僅僅就只是處理分庫分表場景下的數(shù)據(jù)路由工作,它處于數(shù)據(jù)路由層,介于持久層與JDBC之間,因此沒有其它看似有用實則花哨的雞肋功能,并且與應用層集成的方式注定了Kratos必然擁有更好的易用性。

 

對于開發(fā)人員而言,在使用Kratos的時候,就好像是在操作同一個數(shù)據(jù)源一樣,也就是說,之前你的sql怎么寫,換上Kratos之后,業(yè)務邏輯無需做出變動,你只需要關心你的邏輯即可,數(shù)據(jù)路由工作,你完全可以放心的交由Kratos去處理。Kratos站在巨人的肩膀上,必然擁有更大的施展空間。首先Kratos依賴于Spring JDBC,那么如果你習慣使用JdbcTemplate,那就都不是問題,反之你可以先看一下筆者的博文筆者帶你剖析Spring3.x JDBC。其次Kratos目前僅僅只支持Mysql數(shù)據(jù)庫,對于其他RDBMS類型的數(shù)據(jù)庫,基本上以后也不會支持,簡單來說,通用型需求并不是Kratos的目標,做好一件事才是真正的實惠。

 

 kratos的優(yōu)點

1、動態(tài)數(shù)據(jù)源的無縫切換;

2、master/slave一主一從讀寫分離;

3、單線程讀重試(取決于的數(shù)據(jù)庫連接池是否支持);

4、單獨支持Mysql數(shù)據(jù)庫;

5、非Proxy架構,與應用集成,應用直連數(shù)據(jù)庫,降低外圍系統(tǒng)依賴帶來的down機風險;

6、使用簡單,侵入型低,站在巨人的肩膀上,依賴于Spring JDBC;

7、分庫分表路由算法支持2種分片模式,庫內分片/一庫一片;

8、提供自動生成sequenceId的API支持;

9、提供自動生成配置文件的支持,降低配置出錯率;

 

二、互聯(lián)網(wǎng)當下的數(shù)據(jù)拆分過程

對于一個剛上線的互聯(lián)網(wǎng)項目來說,由于前期活躍度并不大,并發(fā)量相對較小,因此企業(yè)一般都會選擇將所有數(shù)據(jù)存放在一個物理DB中進行讀寫操作。但隨著后續(xù)的市場推廣力度不斷加強,活躍度不斷提升,這時如果僅靠一個DB來支撐所有讀寫壓力,就會顯得力不從心。所以一般到了這個階段,大部分Mysql DBA就會將數(shù)據(jù)庫設置為讀寫分離狀態(tài)(一主一從/一主多從),Master負責寫操作,而Slave負責讀操作。按照二八定律,80%的操作更多是讀操作,那么剩下的20%則為寫操作,經(jīng)過讀寫分離后,大大提升了單庫無法支撐的負載壓力。不過光靠讀寫分離并不會一勞永逸,如果活躍度再次提升,相信又會再次遇見數(shù)據(jù)庫的讀寫瓶頸。因此到了這個階段,就需要實現(xiàn)垂直分庫。

 

所謂垂直分庫,簡單來說就是根據(jù)業(yè)務的不同將原本冗余在單庫中的業(yè)務表拆散,分布到不同的業(yè)務庫中,實現(xiàn)分而治之的讀寫訪問操作。當然我們都知道,傳統(tǒng)RDBMS數(shù)據(jù)庫中的數(shù)據(jù)表隨著數(shù)據(jù)量的暴增,從維護性和高響應的角度去看,無論任何CRUD操作,對于數(shù)據(jù)庫而言,都是一件極其傷腦筋的事情。即便設置了索引,檢索效率依然低下,因為隨著數(shù)據(jù)量的暴增,RDBMS數(shù)據(jù)庫的性能瓶頸就會逐漸顯露出來。這一點,Nosql數(shù)據(jù)庫倒是做得很好,當然架構不同,所以不予比較。那么既然已經(jīng)到了這個節(jié)骨眼上了,唯一的殺手锏就是在垂直分庫的基礎之上進行水平分區(qū),也就是我們常說的Sharding。

 

簡單來說,水平分區(qū)要做的事情,就是將原本冗余在單庫中的業(yè)務表分散為N個子表(比如tab_0001、tab_0002、tab_0003、tab_0004...)分別存放在不同的子庫中。理論上來講,子表之間通過某種契約關聯(lián)在一起,每一張子表均按段位進行數(shù)據(jù)存儲,比如tab_0000存儲1-10000的數(shù)據(jù),而tab_0001存儲10001-20000的數(shù)據(jù)。經(jīng)過水平分區(qū)后,必然能夠將原本由一張業(yè)務表維護的海量數(shù)據(jù)分配給N個子表進行讀寫操作和維護,大大提升了數(shù)據(jù)庫的讀寫性能,降低了性能瓶頸。基于分庫分表的設計,目前在國內一些大型網(wǎng)站中應用的非常普遍。

 

當然一旦實現(xiàn)分庫分表后,將會牽扯到5個非常關鍵的問題,如下所示:

1、單機ACID被打破,分布式事務一致性難以保證;

2、數(shù)據(jù)查詢需要進行跨庫跨表的數(shù)據(jù)路由;

3、多表之間的關聯(lián)查詢會有影響;

4、單庫中依賴于主鍵序列自增時生成的唯一ID會有影響;

5、強外鍵(外鍵約束)難以支持,僅能考慮弱外鍵(約定);

 

三、目前市面上常見的一些Sharding中間件產(chǎn)品對比

其實目前市面上的分庫分表產(chǎn)品不在少數(shù),但是這類產(chǎn)品,更多的是基于Proxy架構的方式,在對于不看重通用性的前提下,基于應用集成架構的中間件則只剩下淘寶的TDDL和Mysql官方的Fabric。其實筆者還是非常喜歡TDDL的,Kratos中所支持的庫內分片就是效仿的TDDL,相信大家也看得出來筆者對TDDL的感情。但是TDDL并非是絕對完美的,其弊端同樣明顯,比如:社區(qū)推進力度緩慢、文檔資料匱乏、過多的功能、外圍系統(tǒng)依賴,再加上致命傷非開源,因此注定了TDDL無法為欣賞它的非淘寶系用戶服務。而Fabric,筆者接觸的太少了,并且正式版發(fā)行時間還是太短了,因此就不想當小白鼠去試用,避免出現(xiàn)hold不住的情況。目前常見的一些Shardig中間件產(chǎn)品對比,如圖A-1所示:

圖A-1 常見的Shading中間件對比

 

在基于Proxy架構的Sharding中間件中,大部分的產(chǎn)品基本上都是衍生子Cobar,并且這類產(chǎn)品對分片算法的支持都僅限于一庫一片的分片方式。對于庫內分片和一庫一片等各自的優(yōu)缺點,筆者稍后會進行講解。具體使用什么樣的中間件產(chǎn)品,還需要根據(jù)具體的應用場景而定,當然如果是你正在愁找不到完善的使用手冊、配置手冊之類的文檔資料,Kratos你可以優(yōu)先考慮。

 

四、Kratos的架構原型

簡單來說,分庫分表中間件無非就是根據(jù)Sharding算法對持有的多數(shù)據(jù)源進行動態(tài)切換,這是任何Sharding中間件核心的部分。一旦在程序中使用Kratos后,應用層將會持有N個數(shù)據(jù)源,Kratos通過路由條件進行運算,然后通過Route技術對數(shù)據(jù)庫和數(shù)據(jù)表進行讀寫操作。在此大家需要注意,Kratos內部并沒有實現(xiàn)自己的ConnectionPool,這也就意味著,給了開發(fā)人員極大的自由,使之可以隨意切換任意的ConnectionPool產(chǎn)品,比如你覺得C3P0沒有BonePC性能高,那么你可以切換為BonePC。

 

對于開發(fā)人員而言,你并不需要關心底層的數(shù)據(jù)庫和表的劃分規(guī)則,程序中任何的CRUD操作,都像是在操作同一個數(shù)據(jù)庫一樣,并且讀寫效率還不能夠比之前低太多(比如幾毫秒或者實際毫秒之內完成),而Kratos就承擔著這樣的一個任務。Kratos所處的領域模型定位,如圖A-2所示:

圖A-2 Kratos的領域模型定位

 

 如圖A-2所示,Kratos的領域模型定位處于持久層和JDBC之間。之前筆者曾經(jīng)提及過,Kratos是站在巨人的肩膀上,這個巨人正是Spring。簡單來說,Kratos重寫了JdbcTemplate,并使用了Spring提供的AbstractRoutingDataSource作為動態(tài)數(shù)據(jù)源層。因此從另外一個側面反應了Kratos的源碼注定是簡單、輕量、易閱讀、易維護的,因為Kratos更多的關注點只會停留在Master/Slave讀寫分離層和分庫分表層。我們知道一般的Shading中間件,動不動就幾萬行代碼,其中得“貓膩”有很多,不僅數(shù)據(jù)庫連接池要自己寫、動態(tài)數(shù)據(jù)源要自己寫,再加上一些雜七雜八的功能,比如:通用性支持、多種類型的RDBMS或者Nosql支持,那么代碼自然冗余,可讀性極差。在Kratos中,這些都問題完全會“滾犢子”,因為Kratos只會考慮如何通過Sharding規(guī)則實現(xiàn)數(shù)據(jù)路由。Kratos的3層架構,如圖A-3所示:

圖A-3 Kratos的3層架構

 

既然Kratos只考慮最核心的功能,同時也就意味著它的性能恒定指標還需要結合其他第三方產(chǎn)品,比如Kratos的動態(tài)數(shù)據(jù)源層所使用的ConnectionPool為C3P0,盡管非常穩(wěn)定的,但是性能遠遠比不上使用BonePC,因此大家完全可以將Kratos看做一個高效的黏合劑,它的核心任務就是數(shù)據(jù)路由,你別指望Kratos還能為你處理邊邊角角的零碎瑣事,想要什么效果,自行組合配置,這就是Kratos,一個簡單、輕量級的Sharding中間件。Kratos的應用總體架構,如圖A-4所示:

圖A-4 Kratos的應用總體架構

 

 五、動態(tài)數(shù)據(jù)源層的Master/Slave讀寫分離

當大家對Kratos有了一個基本的了解后,那么接下來我們就來看看如何在程序中使用Kratos。com.gxl.kratos.jdbc.core.KratosJdbcTemplate是Kratos提供的一個Jdbc模板,它繼承自Spring的JdbcTemplate。簡單來說,KratosJdbcTemplate幾乎支持JdbcTemplate的所有方法(除批量操作外)。對于開發(fā)人員而言,只需要將JdbcTemplate替換為KratosJdbcTemplate即可,除此之外,程序中沒有其他任何需要進行修改的地方,這種低侵入性相信大家都應該能夠接受。

 

一般來說,數(shù)據(jù)庫的主從配置,既可以一主一從,也可以一主多從,但目前Kratos僅支持一主一從。接下來我們再來看看如何在配置文件中配置一主一從的讀寫分離操作,如下所示:

Xml代碼  收藏代碼
  1. <import resource="datasource1-context.xml" />  
  2. <aop:aspectj-autoproxy proxy-target-class="true" />  
  3. <context:component-scan base-package="com">  
  4.     <context:include-filter type="annotation"  
  5.         expression="org.aspectj.lang.annotation.Aspect" />  
  6. </context:component-scan>  
  7. <bean id="kJdbcTemplate" class="com.gxl.kratos.jdbc.core.KratosJdbcTemplate">  
  8.     <constructor-arg name="isShard" value="false"/>  
  9.     <property name="dataSource" ref="kDataSourceGroup"/>  
  10.     <property name="wr_weight" value="r1w0"/>  
  11. </bean>  
  12. <bean id="kDataSourceGroup" class="com.gxl.kratos.jdbc.datasource.config.KratosDatasourceGroup">  
  13.     <property name="targetDataSources">  
  14.         <map key-type="java.lang.Integer">  
  15.             <entry key="0" value-ref="dataSource1"/>  
  16.             <entry key="1" value-ref="dataSource2"/>  
  17.         </map>  
  18.     </property>  
  19. </bean>  

   

上述程序實例中,com.gxl.kratos.jdbc.datasource.config.KratosDatasourceGroup就是一個用于管理多數(shù)據(jù)源的Group,它繼承自Spring提供的AbstractRoutingDataSource,并充當了動態(tài)數(shù)據(jù)源層的角色,由此基礎之上實現(xiàn)DBRoute。我們可以看見,在<entry/>標簽中,key屬性指定了數(shù)據(jù)源索引,而value-ref屬性指定了數(shù)據(jù)源,通過這種簡單的鍵值對關系就可以明確的切換到具體的目標數(shù)據(jù)源上。

 

在com.gxl.kratos.jdbc.core.KratosJdbcTemplate中,isShard屬性實際上就是一個Sharding開關,缺省為false,也就意味著缺省是沒有開啟分庫分表的,那么在不Sharding的情況下,我們依然可以使用Kratos來完成讀寫分離操作。在wr_weight屬性中定義了讀寫分離的權重索引,也就是說,我們有多少個主庫,就一定需要有多少個從庫,比如主庫有1個,從庫也應該是1個,因此KratosDatasourceGroup中持有的數(shù)據(jù)源個數(shù)就應該一共是2個,索引從0-1,如果主庫的索引為0,那么從庫的索引就應該為1,也就是“r1w0”。當配置完成后,一旦Kratos監(jiān)測到執(zhí)行的操作為寫操作時,就會自動切換為主庫的數(shù)據(jù)源,而當操作為讀操作的時候,就會自動切換為從庫的數(shù)據(jù)源,從而實現(xiàn)一主一從的讀寫分離操作。

 

六、Kratos的Sharding模型

目前市面上的Sharding中間的分庫分表算法有2種最常見的類型,分別是庫內分片和一庫一片。筆者先從庫內分片開始談起,并且會在后續(xù)進行比較這2種分片算法的優(yōu)劣勢,讓大家更好的進行選擇使用。

 

庫內分片是TDDL采用的一種分片算法,這種分片算法相對來說較為復雜,因為不僅需要根據(jù)路由條件計算出數(shù)據(jù)應該落盤到哪一個庫,還需要計算需要落盤到哪一個子表中。比如我們生產(chǎn)上有32個庫,數(shù)據(jù)庫表有1024張,那么平均每個庫中存放的子表數(shù)量就應該是32個。庫內分片算法示例,如圖A-5所示:

圖A-5 庫內分片

 

一庫一片目前是非常常見的一種分片算法,它同時具備了簡單性和易維護性等特點。簡單來說,一旦通過路由算法計算出數(shù)據(jù)需要落盤到哪一個庫后,就等于間接的計算出了需要落盤到哪一個子表下。假設我們有1024個子表,那么對應的數(shù)據(jù)庫也應該是1024個,當計算出數(shù)據(jù)需要落盤到第105個庫的時候,自然子表也就是第105個。一庫一片算法示例,如圖A-6所示:

圖A-6 一庫一片

 

那么我們究竟在生產(chǎn)中應該選擇庫內分片還是一庫一片呢?盡管Kratos這2種分片算法都同時支持,但生產(chǎn)上所使用的分片算法最好是統(tǒng)一的,千萬別一個子系統(tǒng)的分片算法使用的庫內分片,而另外一個子系統(tǒng)使用的是一庫一片,因為這樣對于DBA來說將會極其痛苦,不僅維護極其困難,數(shù)據(jù)遷移也是一個頭痛的問題。筆者建議優(yōu)先考慮一庫一片這種分片方式,因為這種分片方式維護更簡單,并且在后期數(shù)據(jù)庫擴容時,數(shù)據(jù)遷移工作更加容易和簡單,畢竟算出來庫就等于算出來表,遷移越簡單就意味著生產(chǎn)上停應用的時間更短。當然究竟應該選擇哪一種分片算法,還需要你自行考慮抉擇。

 

七、Sharding之庫內分片

之前筆者已經(jīng)提及過,Kratos的低侵入性設計只需要將原本的JdbcTemplate替換為KratosJdbcTemplate即可,除此之外,程序要不需要修改任何地方,因為讀寫分離操作、分庫分表操作都僅僅只是在配置文件中進行配置的,無需耦合在業(yè)務邏輯中。庫內分片的配置,如下所示:

Xml代碼  收藏代碼
  1. <import resource="datasource1-context.xml" />  
  2. <aop:aspectj-autoproxy proxy-target-class="true" />  
  3. <!-- 自動掃描 -->  
  4. <context:component-scan base-package="com">  
  5.     <context:include-filter type="annotation"  
  6.         expression="org.aspectj.lang.annotation.Aspect" />  
  7. </context:component-scan>  
  8. <bean id="kJdbcTemplate" class="com.gxl.kratos.jdbc.core.KratosJdbcTemplate">  
  9.     <!-- Sharding開關 -->  
  10.     <constructor-arg name="isShard" value="true"/>  
  11.         <property name="dataSource" ref="kDataSourceGroup"/>  
  12.     <!--讀寫權重 -->  
  13.         <property name="wr_weight" value="r32w0"/>  
  14.     <!-- 分片算法模式,false為庫內分片,true為一庫一片 -->  
  15.         <property name="shardMode" value="false"/>  
  16.     <!-- 分庫規(guī)則 -->  
  17.         <property name="dbRuleArray" value="#userinfo_id|email_hash# % 1024 / 32"/>  
  18.     <!-- 分表規(guī)則 -->  
  19.         <property name="tbRuleArray" value="#userinfo_id|email_hash# % 1024 % 32"/>  
  20. </bean>  
  21. <bean id="kDataSourceGroup" class="com.gxl.kratos.jdbc.datasource.config.KratosDatasourceGroup">  
  22.     <property name="targetDataSources">  
  23.         <map key-type="java.lang.Integer">  
  24.             <entry key="0" value-ref="dataSource1"/>  
  25.             <!-- 省略一部分數(shù)據(jù)源... -->  
  26.             <entry key="63" value-ref="dataSource2"/>  
  27.         </map>  
  28.     </property>  
  29. </bean>  

 

上述程序示例中,筆者使用的是一主一從讀寫分離+庫內分片模式。主庫一共是32個(1024個子表,每個庫包含子表數(shù)為32個),那么自然從庫也就是32個,在KratosDatasourceGroup中一共會持有64個數(shù)據(jù)源,數(shù)據(jù)源索引為0-63。那么在KratosJdbcTemplate中首先要做的事情是將分庫分片開關打開,然后讀寫權重索引wr_weight屬性的比例是“r32w0”,這也就意味著0-31都是主庫,而32-63都是從庫,Kratos會根據(jù)這個權重索引來自動根據(jù)所執(zhí)行的操作切換到對應的主從數(shù)據(jù)源上。屬性shardMode其實就是指明了需要Kratos使用哪一種分片算法,true為一庫一片,而false則為庫內分片。

 

屬性dbRuleArray指明了分庫規(guī)則,“#userinfo_id|email_hash# % 1024 / 32”指明了路由條件可能包括兩個,分別為userinfo_id和email_hash。然后根據(jù)路由條件先%tbSize,最后在/dbSize,即可計算出具體的數(shù)據(jù)源。在此大家需要注意,庫的倍數(shù)一定要是表的數(shù)量,否則數(shù)據(jù)將無法均勻分布到所有的子表上?;蛟S大家有個疑問,為什么路由條件會有多個呢?這是因為在實際的開發(fā)過程中,我們所有的查詢條件都需要根據(jù)路由條件來,并且實際情況不可能只有一個理由條件,甚至有可能更多(比如反向索引表)。因此通過符號“|”分隔開多個路由條件。當一條sql執(zhí)行時,Kratos會匹配sql條件中的第一個數(shù)據(jù)庫參數(shù)字段是否是分庫分表條件,如果不是則會拋出異常com.gxl.kratos.jdbc.exception.ShardException。分表規(guī)則“#userinfo_id|email_hash# % 1024 % 32”其實大致和分庫規(guī)則一樣,只不過最后并非是/dbSize,而是%dbSize。經(jīng)過分庫分表算法后,一條sql就會被解析并落盤到指定的庫和指定的表中。

 

八、Sharding之一庫一片

一庫一片類似于庫內分片的配置,但又細微的不同,之前筆者曾經(jīng)提及過,使用一庫一片算法后,根據(jù)路由條件計算出庫后,就等于間接計算出表,那么配置文件中就只需配置分庫規(guī)則即可。一庫一片的配置,如下所示:

Xml代碼  收藏代碼
  1. <import resource="datasource1-context.xml" />  
  2. <aop:aspectj-autoproxy proxy-target-class="true" />  
  3. <!-- 自動掃描 -->  
  4. <context:component-scan base-package="com">  
  5.     <context:include-filter type="annotation"  
  6.         expression="org.aspectj.lang.annotation.Aspect" />  
  7. </context:component-scan>  
  8. <bean id="kJdbcTemplate" class="com.gxl.kratos.jdbc.core.KratosJdbcTemplate">  
  9.     <!-- Sharding開關 -->  
  10.     <constructor-arg name="isShard" value="true"/>  
  11.         <property name="dataSource" ref="kDataSourceGroup"/>  
  12.     <!--讀寫權重 -->  
  13.         <property name="wr_weight" value="r32w0"/>  
  14.     <!-- 分片算法模式,false為庫內分片,true為一庫一片 -->  
  15.         <property name="shardMode" value="true"/>  
  16.     <!-- 分庫規(guī)則 -->  
  17.         <property name="dbRuleArray" value="#userinfo_id|email_hash# % 32"/>  
  18. </bean>  
  19. <bean id="kDataSourceGroup" class="com.gxl.kratos.jdbc.datasource.config.KratosDatasourceGroup">  
  20.     <property name="targetDataSources">  
  21.         <map key-type="java.lang.Integer">  
  22.             <entry key="0" value-ref="dataSource1"/>  
  23.             <!-- 省略一部分數(shù)據(jù)源... -->  
  24.             <entry key="63" value-ref="dataSource2"/>  
  25.         </map>  
  26.     </property>  
  27. </bean>  

 

上述程序示例中,筆者使用的是一主一從讀寫分離+一庫一片模式。主庫一共是32個(32個子表,每個庫包含子表數(shù)為1個),那么自然從庫也就是32個,在KratosDatasourceGroup中一共會持有64個數(shù)據(jù)源,數(shù)據(jù)源索引為0-63。權重索引wr_weight屬性的比例是“r32w0”,這也就意味著0-31都是主庫,而32-63都是從庫,Kratos會根據(jù)這個權重索引來自動根據(jù)所執(zhí)行的操作切換到對應的主從數(shù)據(jù)源上。屬性shardMode其實就是指明了需要Kratos使用哪一種分片算法,true為一庫一片,而false則為庫內分片。

 

屬性dbRuleArray指明了分庫規(guī)則,“#userinfo_id|email_hash# % 32”指明了路由條件可能包括兩個,分別為userinfo_id和email_hash。然后根據(jù)路由條件%dbSize即可計算出數(shù)據(jù)究竟應該落盤到哪一個庫的哪一個子表下。

 

九、自動生成全局唯一的sequenceId

一旦我們分庫分表后,原本單庫中使用的序列自增Id將無法再繼續(xù)使用,那么這應該怎么辦呢?其實解決這個問題并不困難,目前有2種方案,第一種是所有的應用統(tǒng)一調用一個集中式的Id生成器,另外一種則是每個應用集成一個Id生成器。無論選擇哪一種方案,Id生成器所持有的DB都只有一個,也就是說,通過一個通用DB去管理和生成全局以為的sequenceId。

 

目前市面上幾乎所有的Sharding中間件都沒有提供sequenceId的支持,而Kratos的工具包中卻為大家提供了支持。Kratos選擇的是每個應用都集成一個Id生成器,而沒有采用集中式Id生成器,因為在分布式場景下,多一個外圍系統(tǒng)依賴就意味著多一分風險,相信這個道理大家都應該懂。那么究竟應該如何使用Kratos提供的Id生成器呢?首先我們需要單獨準備一個全局DB出來,然后使用Kratos的建表語句,如下所示:

Sql代碼  收藏代碼
  1. CREATE TABLE kratos_sequenceid(  
  2.     k_id INT NOT NULL AUTO_INCREMENT COMMENT '主鍵',  
  3.     k_type INT NOT NULL COMMENT '類型',  
  4.     k_useData BIGINT NOT NULL COMMENT '申請占位數(shù)量',  
  5.     PRIMARY KEY (k_id)  
  6. )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_bin;  

 

當成功建立好生成sequenceId所需要的數(shù)據(jù)庫表后,接下來要做的事情就是進行調用。生成sequenceId,如下所示:

Java代碼  收藏代碼
  1. /** 
  2.  * 獲取SequenceId 
  3.  *  
  4.  * @author gaoxianglong 
  5.  */  
  6. public @Test void getSequenceId() {  
  7.     /* 初始化數(shù)據(jù)源信息 */  
  8.     DbConnectionManager.init("account""pwd""url""driver");  
  9.     System.out.println(SequenceIDManger.getSequenceId(115000));  
  10. }  

 

上述程序示例中,首先需要調用com.gxl.kratos.utils.sequence.DbConnectionManager類的init()方法對數(shù)據(jù)源進行初始化,然后調用com.gxl.kratos.utils.sequence.DbConnectionManager類的getSequenceId()方法即可成功獲取全局唯一的sequenceId。在此大家需要注意,Kratos生成的sequenceId是一個17-19位之間的整型,在getSequenceId()方法中,第一個參數(shù)為IDC機房編碼,第二個參數(shù)為類型操作碼,而最后一個參數(shù)非常關鍵,就是需要向DB中申請的內存占位數(shù)量(自增碼)。

 

簡單來說,相信大家都知道,既然業(yè)務庫都分庫分表了,就是為了緩解數(shù)據(jù)庫讀寫瓶頸,當并發(fā)較高時,一個生成sequenceId的通用數(shù)據(jù)庫能扛得住嗎?筆者告訴你,扛得住!就是因為內存占位。其實原理很簡單,當?shù)谝淮螒脧腎d生成器中去拿sequenceId時,Id生成器會鎖表并將數(shù)據(jù)庫字段k_useData更新為5000,那么第二次應用從Id生成器中去拿sequenceId時,將不會與DB建議物理會話鏈接,而是直接在內存中去消耗著5000內存占位數(shù),直至消耗殆盡時,才會重新去數(shù)據(jù)庫中申請下一次的內存占位。

 

那么問題又來了,如果并發(fā)訪問會不會有問題呢?其實保證全局唯一性有3點,第一是程序中會加鎖,其次數(shù)據(jù)庫會for update,最后每一個操作碼都是唯一的,都會管理自己旗下的內存占位數(shù)(通過Max()函數(shù)比較,累加下一個5000)。或許你會在想,如何提升Id生成器的性能,盡可能的避免與數(shù)據(jù)庫建立物理會話,沒錯,這么想是對的,每次假設從數(shù)據(jù)庫申請的占位數(shù)量是50000,那么性能肯定比只申請5000好,但是這也有弊端,一旦程序出現(xiàn)down機,內存中的內存數(shù)量就會丟失,只能重新申請,這會造成資源浪費。

 

十、自動生成kratos分庫分表配置文件

Kratos的工具包中除了提供有自動生成sequenceId的功能外,還提供有自動生成分庫分表配置文件等功能。筆者自己是非常喜歡這個功能。因為這很明顯可以減少配置出錯率。比如我們采用庫內分片模式,32個庫1024個表,數(shù)據(jù)源配置文件中,需要配置的數(shù)據(jù)源信息會有32個,當然這通過手工的方式也未嘗不可,無非就是一個kratos分庫分表配置文件+一個dataSource文件(如果主從的話,還需要一個從庫的dataSource文件),dataSource文件中需要編寫32個數(shù)據(jù)源信息的<bean/>標簽。但是如果我們使用的是一庫一片這種分片方式,使用的庫的數(shù)量是1024個的時候呢?dataSource文件中需要定義的數(shù)據(jù)源將會是1024個?寫不死你嗎?你能保證配置不會出問題?

 

既然手動編寫配置文件可能會出現(xiàn)錯誤,那么究竟應該如何使用Kratos提供的自動生成配置文件功能來降低出錯率呢?Kratos自動生成配置文件,如下所示:

Java代碼  收藏代碼
  1. /** 
  2.  * 生成核心配置文件 
  3.  *  
  4.  * @author gaoxianglong 
  5.  */  
  6. public @Test void testCreateCoreXml() {  
  7.     CreateXml c_xml = new CreateXml();  
  8.     /* 是否控制臺輸出生成的配置文件 */  
  9.     c_xml.setIsShow(true);  
  10.     /* 配置分庫分片信息 */  
  11.     c_xml.setDbSize("1024");  
  12.     c_xml.setShard("true");  
  13.     c_xml.setWr_weight("r0w0");  
  14.     c_xml.setShardMode("false");  
  15.     c_xml.setDbRuleArray("#userinfo_id|email_hash# % 1024");  
  16.     //c_xml.setTbRuleArray("#userinfo_id|email_hash# % 1024 % 32");  
  17.     /* 執(zhí)行配置文件輸出 */  
  18.     System.out.println(c_xml.createCoreXml(new File("e:/kratos-context.xml")));  
  19. }  
  20.   
  21. /** 
  22.  * 生成數(shù)據(jù)源配置文件 
  23.  *  
  24.  * @author gaoxianglong 
  25.  */  
  26. public @Test void testCreateDadasourceXml() {  
  27.     CreateXml c_xml = new CreateXml();  
  28.     /* 是否控制臺輸出生成的配置文件 */  
  29.     c_xml.setIsShow(true);  
  30.     /* 數(shù)據(jù)源索引起始 */  
  31.     c_xml.setDataSourceIndex(1);  
  32.     /* 配置分庫分片信息 */  
  33.     c_xml.setDbSize("1024");  
  34.     /* 配置數(shù)據(jù)源信息 */  
  35.     c_xml.setJdbcUrl("jdbc:mysql://ip:3306/um");  
  36.     c_xml.setUser("${name}");  
  37.     c_xml.setPassword("${password}");  
  38.     c_xml.setDriverClass("${driverClass}");  
  39.     c_xml.setInitialPoolSize("${initialPoolSize}");  
  40.     c_xml.setMinPoolSize("${minPoolSize}");  
  41.     c_xml.setMaxPoolSize("${maxPoolSize}");  
  42.     c_xml.setMaxStatements("${maxStatements}");  
  43.     c_xml.setMaxIdleTime("${maxIdleTime}");  
  44.     /* 執(zhí)行配置文件輸出 */  
  45.     System.out.println(c_xml.createDatasourceXml(new File("e:/dataSource-context.xml")));  
  46. }  
  47.   
  48. /** 
  49.  * 生成master/slave數(shù)據(jù)源配置文件 
  50.  *  
  51.  * @author gaoxianglong 
  52.  */  
  53. public @Test void testCreateMSXml() {  
  54.     CreateXml c_xml = new CreateXml();  
  55.     c_xml.setIsShow(true);  
  56.     /* 生成master數(shù)據(jù)源信息 */  
  57.     c_xml.setDataSourceIndex(1);  
  58.     c_xml.setDbSize("32");  
  59.     c_xml.setJdbcUrl("jdbc:mysql://ip1:3306/um");  
  60.     c_xml.setUser("${name}");  
  61.     c_xml.setPassword("${password}");  
  62.     c_xml.setDriverClass("${driverClass}");  
  63.     c_xml.setInitialPoolSize("${initialPoolSize}");  
  64.     c_xml.setMinPoolSize("${minPoolSize}");  
  65.     c_xml.setMaxPoolSize("${maxPoolSize}");  
  66.     c_xml.setMaxStatements("${maxStatements}");  
  67.     c_xml.setMaxIdleTime("${maxIdleTime}");  
  68.     System.out.println(c_xml.createDatasourceXml(new File("e:/masterDataSource-context.xml")));  
  69.     /* 生成slave數(shù)據(jù)源信息 */  
  70.     c_xml.setDataSourceIndex(33);  
  71.     c_xml.setDbSize("32");  
  72.     c_xml.setJdbcUrl("jdbc:mysql://ip2:3306/um");  
  73.     c_xml.setUser("${name}");  
  74.     c_xml.setPassword("${password}");  
  75.     c_xml.setDriverClass("${driverClass}");  
  76.     c_xml.setInitialPoolSize("${initialPoolSize}");  
  77.     c_xml.setMinPoolSize("${minPoolSize}");  
  78.     c_xml.setMaxPoolSize("${maxPoolSize}");  
  79.     c_xml.setMaxStatements("${maxStatements}");  
  80.     c_xml.setMaxIdleTime("${maxIdleTime}");  
  81.     System.out.println(c_xml.createDatasourceXml(new File("e:/slaveDataSource-context.xml")));  
  82. }  

 

十一、注意事項

一旦在程序中使用Kratos進行Sharding后,sql的編寫一定要注意,否則將無法進行路由。sql規(guī)則如下所示:

1、暫時不支持分布式事物,因此無法保證事務一致性;

2、不支持多表查詢,所有多表查詢sql,務必全部打散為單條sql分開查詢;

3、不建議使用一些數(shù)據(jù)庫統(tǒng)計函數(shù)、Order by語句等;

4、sql的參數(shù)第一個必須是路由條件;

5、不支持數(shù)據(jù)庫別名;

6、路由條件必須是整型

7、子表后綴為符號"_"+4位整型,比如“tb_0001”——"tb_1024";

 

注意:

目前Kratos還處于1.2階段,未來還有很長的路要走,現(xiàn)在筆者測試環(huán)境上已經(jīng)在大規(guī)模的使用,后續(xù)生產(chǎn)環(huán)境上將會開始投放使用,因此如果各位在使用過程中有任何疑問,都可以通過企鵝群:150445731進行交流,或者獲取Kratos的構件。


    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多