一、MySQL擴(kuò)展具體的實(shí)現(xiàn)方式
隨著業(yè)務(wù)規(guī)模的不斷擴(kuò)大,需要選擇合適的方案去應(yīng)對(duì)數(shù)據(jù)規(guī)模的增長(zhǎng),以應(yīng)對(duì)逐漸增長(zhǎng)的訪問(wèn)壓力和數(shù)據(jù)量。
關(guān)于數(shù)據(jù)庫(kù)的擴(kuò)展主要包括:業(yè)務(wù)拆分、主從復(fù)制,數(shù)據(jù)庫(kù)分庫(kù)與分表。這篇文章主要講述數(shù)據(jù)庫(kù)分庫(kù)與分表
(1)業(yè)務(wù)拆分
在 大型網(wǎng)站應(yīng)用之海量數(shù)據(jù)和高并發(fā)解決方案總結(jié)一二 一篇文章中也具體講述了為什么要對(duì)業(yè)務(wù)進(jìn)行拆分。
業(yè)務(wù)起步初始,為了加快應(yīng)用上線和快速迭代,很多應(yīng)用都采用集中式的架構(gòu)。隨著業(yè)務(wù)系統(tǒng)的擴(kuò)大,系統(tǒng)變得越來(lái)越復(fù)雜,越來(lái)越難以維護(hù),開(kāi)發(fā)效率變得越來(lái)越低,并且對(duì)資源的消耗也變得越來(lái)越大,通過(guò)硬件提高系統(tǒng)性能的方式帶來(lái)的成本也越來(lái)越高。
因此,在選型初期,一個(gè)優(yōu)良的架構(gòu)設(shè)計(jì)是后期系統(tǒng)進(jìn)行擴(kuò)展的重要保障。
例如:電商平臺(tái),包含了用戶、商品、評(píng)價(jià)、訂單等幾大模塊,最簡(jiǎn)單的做法就是在一個(gè)數(shù)據(jù)庫(kù)中分別創(chuàng)建users、shops、comment、order四張表。

但是,隨著業(yè)務(wù)規(guī)模的增大,訪問(wèn)量的增大,我們不得不對(duì)業(yè)務(wù)進(jìn)行拆分。每一個(gè)模塊都使用單獨(dú)的數(shù)據(jù)庫(kù)來(lái)進(jìn)行存儲(chǔ),不同的業(yè)務(wù)訪問(wèn)不同的數(shù)據(jù)庫(kù),將原本對(duì)一個(gè)數(shù)據(jù)庫(kù)的依賴拆分為對(duì)4個(gè)數(shù)據(jù)庫(kù)的依賴,這樣的話就變成了4個(gè)數(shù)據(jù)庫(kù)同時(shí)承擔(dān)壓力,系統(tǒng)的吞吐量自然就提高了。

(2)主從復(fù)制

上圖是網(wǎng)上的一張關(guān)于MySQL的Master和Slave之間數(shù)據(jù)同步的過(guò)程圖。
主要講述了MySQL主從復(fù)制的原理:數(shù)據(jù)復(fù)制的實(shí)際就是Slave從Master獲取Binary log文件,然后再本地鏡像的執(zhí)行日志中記錄的操作。由于主從復(fù)制的過(guò)程是異步的,因此Slave和Master之間的數(shù)據(jù)有可能存在延遲的現(xiàn)象,此時(shí)只能保證數(shù)據(jù)最終的一致性。
(3)數(shù)據(jù)庫(kù)分庫(kù)與分表
我們知道每臺(tái)機(jī)器無(wú)論配置多么好它都有自身的物理上限,所以當(dāng)我們應(yīng)用已經(jīng)能觸及或遠(yuǎn)遠(yuǎn)超出單臺(tái)機(jī)器的某個(gè)上限的時(shí)候,我們惟有尋找別的機(jī)器的幫助或者繼續(xù)升級(jí)的我們的硬件,但常見(jiàn)的方案還是通過(guò)添加更多的機(jī)器來(lái)共同承擔(dān)壓力。
我們還得考慮當(dāng)我們的業(yè)務(wù)邏輯不斷增長(zhǎng),我們的機(jī)器能不能通過(guò)線性增長(zhǎng)就能滿足需求?因此,使用數(shù)據(jù)庫(kù)的分庫(kù)分表,能夠立竿見(jiàn)影的提升系統(tǒng)的性能,關(guān)于為什么要使用數(shù)據(jù)庫(kù)的分庫(kù)分表的其他原因這里不再贅述,主要講具體的實(shí)現(xiàn)策略。請(qǐng)看下邊章節(jié)。
二、分表實(shí)現(xiàn)策略
關(guān)鍵字:用戶ID、表容量
對(duì)于大部分?jǐn)?shù)據(jù)庫(kù)的設(shè)計(jì)和業(yè)務(wù)的操作基本都與用戶的ID相關(guān),因此使用用戶ID是最常用的分庫(kù)的路由策略。用戶的ID可以作為貫穿整個(gè)系統(tǒng)用的重要字段。因此,使用用戶的ID我們不僅可以方便我們的查詢,還可以將數(shù)據(jù)平均的分配到不同的數(shù)據(jù)庫(kù)中。(當(dāng)然,還可以根據(jù)類別等進(jìn)行分表操作,分表的路由策略還有很多方式)
說(shuō)到這里順便給大家推薦一個(gè)Java架構(gòu)方面的交流學(xué)習(xí)群:964357187,里面會(huì)分享一些資深架構(gòu)師錄制的視頻錄像:有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化這些成為架構(gòu)師必備的知識(shí)體系。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源和前輩的面試經(jīng)驗(yàn)和面試題,相信對(duì)于已經(jīng)工作和遇到技術(shù)瓶頸的碼友,在這個(gè)群里會(huì)有你需要的內(nèi)容。
接著上述電商平臺(tái)假設(shè),訂單表order存放用戶的訂單數(shù)據(jù),sql腳本如下(只是為了演示,省略部分細(xì)節(jié)):
CREATE TABLE `order` (
`order_id` bigint(32) primary key auto_increment,
`user_id` bigint(32),
...
)
當(dāng)數(shù)據(jù)比較大的時(shí)候,對(duì)數(shù)據(jù)進(jìn)行分表操作,首先要確定需要將數(shù)據(jù)平均分配到多少?gòu)埍碇?,也就是:表容量?/p>
這里假設(shè)有100張表進(jìn)行存儲(chǔ),則我們?cè)谶M(jìn)行存儲(chǔ)數(shù)據(jù)的時(shí)候,首先對(duì)用戶ID進(jìn)行取模操作,根據(jù) user_id%100 獲取對(duì)應(yīng)的表進(jìn)行存儲(chǔ)查詢操作,示意圖如下:

例如,user_id = 101 那么,我們?cè)讷@取值的時(shí)候的操作,可以通過(guò)下邊的sql語(yǔ)句:
select * from order_1 where user_id= 101
其中,order_1是根據(jù) 101%100 計(jì)算所得,表示分表之后的第一章order表。
注意:
在實(shí)際的開(kāi)發(fā)中,如果你使用MyBatis做持久層的話,MyBatis已經(jīng)提供了很好得支持?jǐn)?shù)據(jù)庫(kù)分表的功能,例如上述sql用MyBatis實(shí)現(xiàn)的話應(yīng)該是:
接口定義:
/**
* 獲取用戶相關(guān)的訂單詳細(xì)信息
* @param tableNum 具體某一個(gè)表的編號(hào)
* @param userId 用戶ID
* @return 訂單列表
*/
public List<Order> getOrder(@Param("tableNum") int tableNum,@Param("userId") int userId);
xml配置映射文件:
<select id="getOrder" resultMap="BaseResultMap">
select * from order_${tableNum}
where user_id = #{userId}
</select>
其中${tableNum} 含義是直接讓參數(shù)加入到sql中,這是MyBatis支持的特性。
注意:
另外,在實(shí)際的開(kāi)發(fā)中,我們的用戶ID更多的可能是通過(guò)UUID生成的,這樣的話,我們可以首先將UUID進(jìn)行hash獲取到整數(shù)值,然后在進(jìn)行取模操作。
三、分庫(kù)實(shí)現(xiàn)策略
數(shù)據(jù)庫(kù)分表能夠解決單表數(shù)據(jù)量很大的時(shí)候數(shù)據(jù)查詢的效率問(wèn)題,但是無(wú)法給數(shù)據(jù)庫(kù)的并發(fā)操作帶來(lái)效率上的提高,因?yàn)榉直淼膶?shí)質(zhì)還是在一個(gè)數(shù)據(jù)庫(kù)上進(jìn)行的操作,很容易受數(shù)據(jù)庫(kù)IO性能的限制。
因此,如何將數(shù)據(jù)庫(kù)IO性能的問(wèn)題平均分配出來(lái),很顯然將數(shù)據(jù)進(jìn)行分庫(kù)操作可以很好地解決單臺(tái)數(shù)據(jù)庫(kù)的性能問(wèn)題。
分庫(kù)策略與分表策略的實(shí)現(xiàn)很相似,最簡(jiǎn)單的都是可以通過(guò)取模的方式進(jìn)行路由。
還是上例,將用戶ID進(jìn)行取模操作,這樣的話獲取到具體的某一個(gè)數(shù)據(jù)庫(kù),同樣關(guān)鍵字有:
用戶ID、庫(kù)容量
路由的示意圖如下:

上圖中庫(kù)容量為100。
同樣,如果用戶ID為UUID請(qǐng)先hash然后在進(jìn)行取模。
四、分庫(kù)與分表實(shí)現(xiàn)策略
上述的配置中,數(shù)據(jù)庫(kù)分表可以解決單表海量數(shù)據(jù)的查詢性能問(wèn)題,分庫(kù)可以解決單臺(tái)數(shù)據(jù)庫(kù)的并發(fā)訪問(wèn)壓力問(wèn)題。
有時(shí)候,我們需要同時(shí)考慮這兩個(gè)問(wèn)題,因此,我們既需要對(duì)單表進(jìn)行分表操作,還需要進(jìn)行分庫(kù)操作,以便同時(shí)擴(kuò)展系統(tǒng)的并發(fā)處理能力和提升單表的查詢性能,就是我們使用到的分庫(kù)分表。
分庫(kù)分表的策略相對(duì)于前邊兩種復(fù)雜一些,一種常見(jiàn)的路由策略如下:
1、中間變量 = user_id%(庫(kù)數(shù)量*每個(gè)庫(kù)的表數(shù)量);
2、庫(kù)序號(hào)?。健∪≌ㄖ虚g變量/每個(gè)庫(kù)的表數(shù)量);
3、表序號(hào) = 中間變量%每個(gè)庫(kù)的表數(shù)量;
例如:數(shù)據(jù)庫(kù)有256 個(gè),每一個(gè)庫(kù)中有1024個(gè)數(shù)據(jù)表,用戶的user_id=262145,按照上述的路由策略,可得:
1、中間變量 = 262145%(256*1024)= 1;
2、庫(kù)序號(hào)?。健∪≌?/1024)= 0;
3、表序號(hào)?。健?%1024 = 1;
這樣的話,對(duì)于user_id=262145,將被路由到第0個(gè)數(shù)據(jù)庫(kù)的第1個(gè)表中。
示意圖如下:

五、分庫(kù)分表總結(jié)
關(guān)于分庫(kù)分表策略的選擇有很多種,上文中根據(jù)用戶ID應(yīng)該是比較簡(jiǎn)單的一種。其他方式比如使用號(hào)段進(jìn)行分區(qū)或者直接使用hash進(jìn)行路由等。有興趣的可以自行查找學(xué)習(xí)。
關(guān)于上文中提到的,如果用戶的ID是通過(guò)UUID的方式生成的話,我們需要單獨(dú)的進(jìn)行一次hash操作,然后在進(jìn)行取模操作等,其實(shí)hash本身就是一種分庫(kù)分表的策略,使用hash進(jìn)行路由策略的時(shí)候,我們需要知道的是,也就是hash路由策略的優(yōu)缺點(diǎn),優(yōu)點(diǎn)是:數(shù)據(jù)分布均勻;缺點(diǎn)是:數(shù)據(jù)遷移的時(shí)候麻煩,不能按照機(jī)器性能分?jǐn)倲?shù)據(jù)。
上述的分庫(kù)和分表操作,查詢性能和并發(fā)能力都得到了提高,但是還有一些需要注意的就是,例如:原本跨表的事物變成了分布式事物;由于記錄被切分到不同的數(shù)據(jù)庫(kù)和不同的數(shù)據(jù)表中,難以進(jìn)行多表關(guān)聯(lián)查詢,并且不能不指定路由字段對(duì)數(shù)據(jù)進(jìn)行查詢。分庫(kù)分表之后,如果我們需要對(duì)系統(tǒng)進(jìn)行進(jìn)一步的擴(kuò)陣容(路由策略變更),將變得非常不方便,需要我們重新進(jìn)行數(shù)據(jù)遷移。
六、總結(jié)
上述中,我們學(xué)到了如何進(jìn)行數(shù)據(jù)庫(kù)的讀寫(xiě)分離和分庫(kù)分表,那么,是不是可以實(shí)現(xiàn)一個(gè)可擴(kuò)展、高性能、高并發(fā)的網(wǎng)站那?很顯然還不可以!一個(gè)大型的網(wǎng)站使用到的技術(shù)遠(yuǎn)不止這些,可以說(shuō),這些都是其中的最基礎(chǔ)的一個(gè)環(huán)節(jié),因?yàn)檫€有很多具體的細(xì)節(jié)我們沒(méi)有掌握到,比如:數(shù)據(jù)庫(kù)的集群控制,集群的負(fù)載均衡,災(zāi)難恢復(fù),故障自動(dòng)切換,事務(wù)管理等等技術(shù)。因此,還有很多需要去學(xué)習(xí)去研究的地方,大家也可以進(jìn)我的私人技術(shù)交流群【Java高級(jí)互聯(lián)網(wǎng)架構(gòu):964357187】與更多的阿里京東大佬交流、切磋技術(shù)。
總之:
路漫漫其修遠(yuǎn)兮,吾將上下而求索。
前方道路美好而光明,2019年新征程,不泄步!