日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

系統(tǒng)架構(gòu)-性能篇章2(系統(tǒng)拆分1)

 昵稱9552892 2015-04-25

系統(tǒng)為什么拆分?

系統(tǒng)做大了,并發(fā)量無法扛得住,如何做?

業(yè)務(wù)做復(fù)雜了,單個應(yīng)用中不能個性化,如何做?

模塊和邏輯對各類資源開銷非常特殊,如何做?

。。。。。。

拆分、拆分、再拆分。

由 全世界用一個系統(tǒng)表達(dá)全世界所有的企業(yè)和公司的業(yè)務(wù)開始,注定系統(tǒng)做大后必然拆分的走向,也就是一個大力士無法完成成千上萬群眾所能做到的一件大事,高集 成度的硬件和軟件解決方案,為傳統(tǒng)企業(yè)提供較為完善的解決方案,并在這種程度上是可以節(jié)約成本,高端機和高端存儲的解決方案,當(dāng)達(dá)到一個成本的交叉點后,隨著數(shù)據(jù)量以及并發(fā)量的不斷上升,其解決方案的成本也會隨之直線上漲。

 

如何拆分?拆分后有什么后果,是其中一個問題?

 

首先我們看看應(yīng)用一般是如何拆分的:

1、應(yīng)用一般的企業(yè)內(nèi)部都是按照業(yè)務(wù)增長方式比較多,所以隨著業(yè)務(wù)的增加,將系統(tǒng)進行拆分的是比較多的,也就是將一個較大的系統(tǒng)拆分為多個小的系統(tǒng)。

 

2、在一些企業(yè)中,不愿意將系統(tǒng)拆分為小系統(tǒng)(原因后面說明),而是將所有的內(nèi)容部署在一起,依賴于集群分發(fā)到多個節(jié)點上去做負(fù)載均衡,這樣來完成一種切割,前序兩種也就是應(yīng)用系統(tǒng)級別的縱橫向切割。

 

3、 獨立工具、模塊、服務(wù)的獨立化和集群化,基于SOA服務(wù)的企業(yè)級應(yīng)用,很多模塊經(jīng)過抽象后,并非子系統(tǒng),而是一個獨立的服務(wù)系統(tǒng),不參與業(yè)務(wù),只參與一個技術(shù)級別的功能服務(wù),如MQ、JMS、MemCached等,我們經(jīng)常也管這一類叫做中間件,也就是平臺沒有提供自己來做或第三方提供的中間件(當(dāng)然中間 件也包含應(yīng)用服務(wù)器)。

 

4、數(shù)據(jù)庫拆分,數(shù)據(jù)庫拆分是也是因為壓力上升,以及存儲容量的需求,最終在成本上認(rèn)為拆分是必然的走勢;數(shù)據(jù)庫拆分有多重規(guī)則存在。

 

5、由于上述各類拆分導(dǎo)致的運維的困難,在數(shù)以萬計的計算機集群下,如何動態(tài)資源分配和拆分以及拋開分布式的內(nèi)部細(xì)節(jié)來編程,如何自動化運維系統(tǒng)就是大型計算機集群下需要考慮的問題-云存儲與云計算。

 

我們拆分中面臨哪些問題?(這些內(nèi)容在后面的文章中說明,本文不再闡述)

1、負(fù)載均衡器的問題。

2、不同系統(tǒng)之間的通信問題。

3、數(shù)據(jù)寫入和查找的問題。

4、跨數(shù)據(jù)庫事務(wù)問題。

5、跨數(shù)據(jù)庫序列問題。

6、不同應(yīng)用的本地緩存問題。

7、系統(tǒng)之間的直接依賴和間接依賴問題。

8、獨立模塊面臨的單點問題。

9、各類批量分組、切換、擴展的問題。

10、統(tǒng)一監(jiān)控和恢復(fù)問題。

 

本 文我們暫時不討論關(guān)于云存儲方面的問題,先引入話題,不過每項技術(shù)的產(chǎn)生都是為了解決某些特定的問題而存在,所以云也并非萬能的,后面的文章我們會介紹一 些基于純java開發(fā)的hadoop相關(guān)架構(gòu)和模塊(如:MapReduce、Hbase、Hive、HDFS、Pig等子系統(tǒng),說明當(dāng)今海量信息的互聯(lián) 網(wǎng)中大象的崛起)。

 

1、系統(tǒng)按照業(yè)務(wù)拆分

首先看下企業(yè)中拆分為小系統(tǒng)的過程中的過程和遇到的問題,在大多數(shù)企業(yè)中,選擇高端企業(yè)的解決方案,因為一臺兩臺小型機一般的企業(yè)都沒有問題,除非是做的項目的確太小了,這類系統(tǒng)的訪問量大概每天保持在幾十萬左右高的有一百多萬的,不過為什么要拆分,一般有以下原因

   a.隨著業(yè)務(wù)的發(fā)展,模塊之間的耦合性越來越強

   b.開發(fā)人員越來越多,相互之間代碼版本也難以管理

   c.系統(tǒng)啟動加載PermGen的時間也會很長并且需要很大的PermGen,更加重要的原因是JVM在CMSGC出來之前管理大內(nèi)存是有問題的

   d.尤其是發(fā)生Full GC時候在大內(nèi)存的JVM上暫停時間是相當(dāng)?shù)拈L,長得讓人無法接受.

   e.在單個機器上硬件廠商做得集成度越高,算法就越來越復(fù)雜,尤其是CPU的個數(shù)始終有限,這樣就導(dǎo)致的是單位時間內(nèi)處理的請求數(shù)也就受到限制,拆分水平擴展是非常容易的.

   f.一個大系統(tǒng)由多個開發(fā)商完成,多個開發(fā)商都有自己的主打產(chǎn)品,為自己節(jié)約成本,將各個產(chǎn)品以集成的方法完成一個大系統(tǒng)的業(yè)務(wù)過程。

  等等原因。

 

那 么系統(tǒng)拆分這樣的系統(tǒng)拆分有什么技巧嗎,可以說原因就算是技巧,也就是在什么時候再拆分,一般系統(tǒng)我們能不拆分就不拆分,因為拆分有有很多麻煩要去面對, 面臨的第一個困難就是以前一個工程內(nèi)部的系統(tǒng),相互之間的調(diào)用就可以直接調(diào)用到,現(xiàn)在很麻煩,要兩邊來做接口,接口還得聯(lián)調(diào),聯(lián)調(diào)是一件比較惡心的事情,尤其是兩個廠商之間來聯(lián)調(diào)。

 

所以拆分應(yīng)當(dāng)具有的最基本條件是高內(nèi)聚、 低耦合的條件,也就是說,這個系統(tǒng)和外部系統(tǒng)的調(diào)用模塊對于整個系統(tǒng)的模塊來講是比較少的,而不是大部分模塊都是在和外部系統(tǒng)交互,除了專門用于處理系統(tǒng)交互的系統(tǒng)外,這樣的拆分設(shè)計是肯定不合理的,因為通信的代價遠(yuǎn)遠(yuǎn)大于本地JVM的代價。

 

開 發(fā)人員越來越多,從最初的一個人,幾個人,到幾十人,幾百人甚至上千人,在一個工程中來寫代碼是很恐怖的事情,誰改了沒法查出來,無法定位,很亂,所以拆 分在一定程度上可以將版本控制的粒度細(xì)化一下,但是并不代表拆分后就沒有版本問題;隨著產(chǎn)品不斷模塊化和抽象化,在大多數(shù)的應(yīng)用中,獨立的子系統(tǒng)就會成為一個獨立的行業(yè)產(chǎn)品,可以基于配置模式的適用于大部分的地區(qū)工廠或者企業(yè)的應(yīng)用,也可以通過一個頂層版本分發(fā)出來的多個地區(qū)化個性化版本(可能有兩層結(jié) 構(gòu));也就是在節(jié)約大部分共享勞動力的基礎(chǔ)上如何做到個性化平臺,這也是行業(yè)軟件中非常盛行的,不過這樣將絕大部分程序員控制在一個小匣子里面了,幾乎沒有發(fā)揮的空間。

 

上面也說了,系統(tǒng)可能由幾十人、幾百人甚至于上千人去 寫,如果大家都寫一個工程,代碼量可想而知,系統(tǒng)初始化需要加載代碼段到PermGen,這塊空間將不可預(yù)知的大小發(fā)展,并且隨著業(yè)務(wù)的復(fù)雜性,需要的引 入的第三方技術(shù)越來越多,第三發(fā)包的class同樣會占用PermGen的空間,不用多說,這塊空間的大小是不可預(yù)知的。

 

當(dāng) 發(fā)生Full GC的時候,遍歷整個內(nèi)存,在沒有CMS GC出來之前,或者現(xiàn)在G 1的出現(xiàn),F(xiàn)ull GC對于幾十G上百G的大內(nèi)存是一件非常痛苦的事情,延遲時間可以打到十幾秒甚至于上百秒(這里在16個4 core的CPU使用了并行GC,時間是應(yīng)用暫停時間),這是不可以接受的,雖然CMS GC已經(jīng)可以在較短的暫停時間內(nèi)回收掉大內(nèi)存(只是暫停時間減少,但是回收時間可能會更加長),不過在它目前解決的主要問題是這個,同時由于大內(nèi)存部署邏輯節(jié)點的個數(shù)減少,使得負(fù)載均衡器的負(fù)載目標(biāo)成倍減少,這樣可以讓同樣的負(fù)載均衡器支撐起更加龐大的后臺訪問集群;不過大部分早期的系統(tǒng)還沒有看到CMS GC的誕生,更加沒有想到G1會出現(xiàn)(其實早在N多年前,論文就出來了,只是一直沒有實現(xiàn)而已),所以一直都還是在沿用比較老的拆分方法,不過拆分始終是 有它的好處的,不僅僅是因為GC的問題,在傳統(tǒng)企業(yè)中一般的負(fù)載均衡器也足以支撐,不會面臨更大的問題。

 

對 于集成度較高的,通過芯片等方式來完成高性能的服務(wù)方法,對于傳統(tǒng)軟件來講是非常好的,因為通過硬件完成的,一般情況下比軟件完成的速度要快(所謂通過硬 件完成除了通過集成電路增加各類特殊指令外,還有就是基于芯片或底層語言實現(xiàn)使之效能更高而且封裝操作),不過遇到的問題就是隨著集成度的高度集中,算法越來越復(fù)雜,導(dǎo)致了內(nèi)部的諸多沖突,水平擴展性受到了嚴(yán)重的限制,所以幾乎沒有多少算法的拆分,是一個必然的發(fā)展趨勢。

 

多 個開發(fā)商完成了一個自己的系統(tǒng),開發(fā)商為了產(chǎn)品化系統(tǒng),并且由于系統(tǒng)的復(fù)雜性,以及提升開發(fā)商在行業(yè)內(nèi)部的積淀,所以就需要不斷完善產(chǎn)品,不斷版本化,以 及本地化的不斷改善;這個目的是好的,不過一定要做好版本的事情,以及一個大型的行業(yè)軟件的頂層架構(gòu)以及繼承關(guān)系,否則不如不做,部分軟件廠商可能只考慮到前者,也就是產(chǎn)品化,不過代碼頂層架構(gòu)幾乎沒有,只有業(yè)務(wù)架構(gòu),產(chǎn)品化和本地化代碼更加是隨心所欲,軟件五花八門,就像貼補丁一樣,誰要做本地就加一個 else if,甚至于有直接對地區(qū)判定的硬代碼,很無語的做法,我個人認(rèn)為這樣做不如直接拿一個模板來改出來一個系統(tǒng),就不要做什么版本,因為這樣的版本的代碼是越來越爛,面對這種代碼唯一的辦法就是重構(gòu),如果不想重構(gòu)就永遠(yuǎn)下去吧,不面臨改變終究會被淘汰掉;這種情況也面臨在系統(tǒng)底層版本升級上,包括JDK的升 級,如果只是考慮到成本和風(fēng)險的話應(yīng)該說真的永遠(yuǎn)都無法升級,沒有做不到的升級,關(guān)鍵是否愿意去做,越晚去升級,所帶來的成本代價是越高的,類似國際上有多少大型軟件的底層版本也是在不斷的升級中,而上層的代碼由于繁雜而不斷的重構(gòu),雖然說不一定要時時刻刻重構(gòu),這樣程序員也會很累,并且也體現(xiàn)不出他們的 價值,因為成天就是改代碼,但是該重構(gòu)就應(yīng)該要去重構(gòu)。

負(fù)載均衡,首先負(fù)載均衡可以是硬件也可以是軟件,硬件在一定程度上支撐不上去的時候就要考慮通過軟件的負(fù)載均衡來解決了(硬件一般情況下比軟件要快速,但是它本身設(shè)計的復(fù)雜性導(dǎo)致了在一定場景下不如軟件的擴展性好),系統(tǒng)在拆分后不論是分布到各個機器上還是在一個機器上虛擬出來多個節(jié)點都是需要,將其負(fù)載均衡的,按照URL和端口+虛擬目錄名稱進行負(fù)載,負(fù)載均衡器需要 知道這些內(nèi)容,也需要知道那個session訪問了那臺機器,中間負(fù)載均衡器會采用一些特殊的算法來解決一些問題,這里簡單介紹到這里,在下一篇文章中會 介紹下負(fù)載均衡的大致原理和作用。

負(fù)載均衡器并不簡單承擔(dān)這個問題,在負(fù)載均衡器中一般還會有很多算法存在,如負(fù)載均衡器比較經(jīng)典的算法就是一致性hash算法,或者輪訓(xùn)檢測;而在有限的線程下,為了得到更大的連接訪問,異步IO的訪問策略應(yīng)運而生,著名的Nginx到目前為止都是全世界大型互聯(lián)網(wǎng)前端負(fù)載均衡的設(shè)計藍(lán)圖的標(biāo)準(zhǔn),其QPS極限情況可以打到30000-50000左右,內(nèi)部還存在各種模式來支持不同的情況(NAT、DR、RUN),當(dāng)然還有很多類似的負(fù)載均衡設(shè)備(設(shè)計上有些差別)。

2、系統(tǒng)水平拆分:系統(tǒng)水平拆分即同一個子系統(tǒng),或者整個系統(tǒng)部署在多個node上,這些node可以是多個主機或同一個主機上的多個軟件節(jié)點;但是這些節(jié)點目前來講即使應(yīng)用拆分得再細(xì),在分布式系統(tǒng)上的這種低端機器也不可能扛得住高并發(fā)的訪問,一般這類低端服務(wù)器代碼調(diào)解得較好等各種情況下,服務(wù)器的QPS一般都是保持在200以內(nèi)的(這是以16個CPU來處理,一個請求在80ms內(nèi)處理完成請求分派,業(yè)務(wù)處理和數(shù)據(jù)請求,反饋結(jié)果等過程已經(jīng)是非??焖俚牧耍芏鄷r候一個SQL都會超過這個時間),當(dāng)然單用幾個字節(jié)去做壓力測試,反饋幾個字節(jié),并且中間幾乎沒有IO方面的額請求(如數(shù)據(jù)庫、文件、遠(yuǎn)程方法調(diào)用等),那么這個QPS你可能會上千,甚至于在好的機器上可以上萬也有可能。

也就是系統(tǒng)真正運行的時候,前端的用的并發(fā)量都是有限的,而且很多時候代碼不好的時候,一般應(yīng)用的QPS還會更低;面對高并發(fā),在這種情況下,我們唯一可以做的就是加機器,也就是水平擴展,它的分發(fā)也是依賴于負(fù)載均衡設(shè)備,加機器的過程就好比是工廠里面的請很多工人來做同一件事情一樣,相對來講第一種拆分就是請不同的人來做不同的事情,不要讓一個人從頭做到尾部,那樣會搞得很累,而且對人的要求也很高。

這種拆分沒有什么太高的要求,只要負(fù)載均衡設(shè)備可以支撐就可以,為了讓負(fù)載均衡可以支撐更大的壓力,那么就盡量讓節(jié)點數(shù)量減少,那么就希望在同一臺實體機器上盡量一個節(jié)點(通過對實體機器進行虛擬化可以在某些情況下節(jié)約成本,并將物理機本身的性能發(fā)揮到一個極限,并可以將一個比較好一點的機器分?jǐn)偨o多個訪問量較低的系統(tǒng),不過虛擬化本身也會產(chǎn)生很多開銷,在這些方面需要綜合權(quán)衡一下好壞),可惜目前來講Oracle的Hotspot VM還不足以支撐大型的非常好的實時系統(tǒng)(我們很多時候不得不在同一個大內(nèi)存機器上部署多個小的JVM節(jié)點),尤其面對幾種場景顯得力不從心:

1、大內(nèi)存的管理(包括GC、內(nèi)存分析、跟蹤等都沒有完善的體系和解決方案)。

2、做實時應(yīng)用不適合,實時應(yīng)用的延遲一般是毫秒級別(如2ms響應(yīng),最慢也不能有十多毫秒的響應(yīng),當(dāng)然這種不包含IO操作,只是做一些內(nèi)存處理和反饋,并且數(shù)據(jù)量不大),而java在正常情況下,如果一旦發(fā)生GC,即使并行GC,而且僅僅只針對Yong空間做GC,也需要一段延遲(在一個16CPU的機器上,配置了并行GC,發(fā)生YGC的時候(Yong的大小大概為330M左右),延遲大概為10ms-15ms左右,發(fā)生Full GC的時候(Heap大小為1.5G),延遲大概為30ms-40ms左右),如果是更大的內(nèi)存,就更加蠻了,因為回收的時間很多時候取決于內(nèi)存的大小,增加一倍的內(nèi)存,并不代表回收時間只增加一倍,因為隨著內(nèi)存的增加,回收過程中產(chǎn)生的開銷和沖突也變化,所以內(nèi)存增加一倍,時間不一定只增加一倍,曾經(jīng)在96G的JVM內(nèi)存上,采用16CPU進行全局GC,大概需要3分多鐘,也就是說這3分多鐘外部是無法訪問的,在實時應(yīng)用面前這就是垃圾。

3、做緩存不適合,分代垃圾回收考慮的是絕大部分對象都應(yīng)該死掉,而Old會采用全局GC,即使是CMS也會有各種問題;很多時候我們在緩存的時候,數(shù)據(jù)初始化就會裝載進去,而很少甚至于不用去做GC,至少可以說99%的內(nèi)存是不需要考慮GC的;而且做緩存的服務(wù)器內(nèi)存都是大內(nèi)存,也就是沒有地方讓自己來操控可存放不做GC的內(nèi)容,但是程序員發(fā)現(xiàn)這部分內(nèi)容占據(jù)了絕大部分內(nèi)存而自己卻無法控制它。

4、目前不支持半長命對象,也就是要么是長命鬼、要么是短命鬼,但是很多非常規(guī)應(yīng)用中,有很多半長命對象,采用不同的算法,會提高更好的性能,如一些page cache數(shù)據(jù),在內(nèi)存中啟用LRU策略,這些隊列的數(shù)據(jù),不能說他們的壽命很長,也不能說他們的壽命很短,但是LRU本身在使用的過程中,不想受到類似Yong和Old之間的這種晉升策略,因為放在Eden中覺得命太短,來回倒騰,有很多還是會到Old中(具體有多少進入old要看應(yīng)用場景),進入old它又并不是什么太長命的東西,隨時可能就掛掉了,真是無奈啊。

其實還有很多JVM不方便去做的服務(wù)器方面的特殊應(yīng)用,不過隨著JVM的發(fā)展已經(jīng)比以前有了很大的飛躍,而且越來越多的硬件廠商和學(xué)術(shù)界的頂尖高手在為java的發(fā)展而努力,所以我很期待java能解決掉這些問題。

3、 獨立工具、模塊、服務(wù)的獨立化和集群化

其實這種拆分和第一種拆分有相似之處,幾乎可以算是一樣的拆分模式,不過說到工具化、模塊化、服務(wù)化、集群化,這種屬于更為專業(yè)的拆分,第一種拆分的依據(jù)是系統(tǒng)各項壓力上來,為考慮擴展性,而不得不拆分系統(tǒng),而將很多高內(nèi)聚、低耦合的系統(tǒng)拆分出來,也就是模塊成為了子系統(tǒng)。

而這種拆分是一種技術(shù)獨立性拆分,將很多較為復(fù)雜,不好解決的技術(shù)以及工具特征獨立出來,虛擬化為一種服務(wù)模式,為外部提供服務(wù),你可以將它理解為一個傳統(tǒng)的子系統(tǒng),不過它是屬于很多系統(tǒng)里面都需要的一個公共子系統(tǒng),而前者僅僅一般只為自己的兄弟模塊提供相應(yīng)的服務(wù)以及一些自己的對外用戶服務(wù);比如:將郵件系統(tǒng)獨立、短信系統(tǒng)獨立就是為很多應(yīng)用服務(wù),大家都可以使用,將通信技術(shù)獨立、將分布式緩存獨立、將配置管理獨立、將版本管理獨立就是屬于技術(shù)上的獨立進而逐步個性化成為一種服務(wù)。

第一種和這種拆分方法沒有明顯的區(qū)別,可以說這種拆分的思想是受第一種拆分的影響或者說基于它來做的,它的目的是以一個個體或者集群為外部提供一種公共服務(wù);當(dāng)一個企業(yè)或者一個大的互聯(lián)網(wǎng)公司,將這些公共服務(wù)開放出來后,形成一種全局的數(shù)據(jù)、技術(shù)的服務(wù)平臺。

4、數(shù)據(jù)庫拆分

這個話題扯得有點大了,因為數(shù)據(jù)庫拆分這個拆分方法的確太多,本文也不能完全說明數(shù)據(jù)庫的拆分方法,只是概要性的提及一些內(nèi)容。

首先,前端有壓力,數(shù)據(jù)庫自然也有,而且數(shù)據(jù)庫壓力肯定比前端壓力會更多(當(dāng)然前端可以采用很多緩存技術(shù)來環(huán)節(jié)數(shù)據(jù)庫的壓力),數(shù)據(jù)庫的復(fù)雜性比前端要更多,因為它是數(shù)據(jù)的核心,需要對數(shù)據(jù)庫的安全、一致性等做保障,在很多處理上它都是采用磁盤IO操作,而普通的sata盤是很爛的,sas盤可能會稍微好一些,這些盤上做幾個KB的IOPS測試,一般只能達(dá)到180的IOPS就很不錯了,當(dāng)然根據(jù)磁盤本身的尺寸和轉(zhuǎn)速會有所區(qū)別;在早期的技術(shù)上,我們大部分的都是采用高端存儲,如:EMC、IBM這類公司就是專門做高端存儲的,其IOPS可以達(dá)到萬級別,其實其原理也是在普通存儲級別上做了很多類似多存儲控制器、鏡像、cache等技術(shù)來解決很多問題,但是其價格非常昂貴,小型機+EMC的解決方案相信是諸多企業(yè)的絕佳解決方案,因為根本不用擔(dān)心性能、穩(wěn)定性和存儲空間,但是在數(shù)據(jù)量達(dá)到非常大的時候,他們也會顯得力不從心,此時在這種解決方案下也不得不去拆分,拆分過程中出現(xiàn)的問題就需要技術(shù)人員來解決,付出的成本將是指數(shù)級的上升,而不是平衡上升的;SSD的出現(xiàn)雖然顛覆了傳統(tǒng)的磁盤存取效率(主要是隨機存取效率,順序讀寫優(yōu)勢并不大),不過目前還有很多問題存在,最近Intel也稱其發(fā)生過丟失數(shù)據(jù)的問題,而且SSD目前的成本非常高,不過我們可以看到它的來臨是傳統(tǒng)磁盤開始被取代的標(biāo)志。

好,OK,隨著磁盤性能提高,但是容量還是和以前差不多,而且更加貴,所以就當(dāng)前來講我們絕大部分還是用傳統(tǒng)磁盤來解決,在這種一塊磁盤做一百多的IOPS的情況下(注意一個SQL并不代表只做一次IO,具體做多少次IO和實際的應(yīng)用場景、實際的優(yōu)化方案、以及SQL的寫法所決定;而一個業(yè)務(wù)請求也一般會做多個SQL操作),我們?nèi)绾翁岣邤?shù)據(jù)庫的性能呢?和上面一樣,在很多時候我們先選擇的是小機+高端存儲的解決方案;但是隨著復(fù)雜性的增加,成本開始補課預(yù)測,所以為了接觸這種耦合性,我們需要一種高可擴展的分布式技術(shù)來解決,在多個分布式的機器上來解決這些問題。

首先,這種可以認(rèn)為是一種分區(qū)技術(shù)在分布式上的一種實現(xiàn),也就是將原有分區(qū)的技術(shù)應(yīng)用在多臺機器上,按照一種規(guī)則拆分到多臺計算機上,如果跨機器查找也就是原來的跨分區(qū)查找,顯然性能不如在單個機器上查找快速,所以如何設(shè)計分區(qū)成為一個性能關(guān)鍵,而不是僅僅為了拆分而拆分;另外拆分之前要有預(yù)算,計算所需要的TPS、QPS等負(fù)載情況,拆分到多少個機器上可以承受得起這樣的訪問量,一般最少需要預(yù)留一半的余量才可以預(yù)防突發(fā)性事件,如果需要未來幾年都不受到拆分上的干擾,那么就可以預(yù)留更多;因為這種數(shù)據(jù)庫拆分的代價是很高的。

在早期的數(shù)據(jù)庫拆分中,有主備讀寫分離,ORACLE RAC多實例運算,不過面對越來越龐大的系統(tǒng),他們都顯得力不從心了,當(dāng)然讀寫分離還是可以和現(xiàn)有的人工拆分所兼容,人工拆分主要是為了得到更好的水平擴展。

首先我們來看看傳統(tǒng)應(yīng)用中的range分區(qū),如果用在分布式上,就是放在多個主機上的多個庫上的多個表,這種用于自動增長列或時間上比較多,如剛開始可以將1-1億的數(shù)據(jù)一般可以用多久,選擇多少個機器來做,每臺機器可以存放多少數(shù)據(jù),而這種拆分Insert操作始終落在最后一臺機器的最后一個表的最后一個block上,而且在剛開始使用的時候,后面所有的機器的所有的表都是空的,沒有任何用處,顯得非常的浪費,也就是沒有數(shù)據(jù)的機器一直都是閑著的;這個問題比較好解決,你可以用一個無窮大來代表最后一臺機器,當(dāng)覺得應(yīng)該加機器的時候,再將最后前一臺機器的上限控制住,不過前一個問題是沒辦法搞定的,所以這種方法是用在insert壓力并不是很大的情況,每秒要是有幾千個insert這樣做肯定是不行的,其余的update、delete等如果有最近訪問熱點,那么最后一臺機器也必將成為熱點訪問區(qū)域,一般最近訪問的都是熱點,不過這種思路最容易讓人接受,而且最容易做出來。

那么在大部分應(yīng)用中我們?yōu)榱丝紤]負(fù)載較為均衡,所以我們選擇hash算法,但是絕對不是一致性hash算法,因為一致性hash在擴展時會導(dǎo)致數(shù)據(jù)不一致,一些數(shù)學(xué)模型可以解決,但是非常復(fù)雜而且也會存在數(shù)據(jù)版本的問題;hash算法最簡單的就是求模,如將一個表拆分為100個表,那么按照絕大部分情況按照某個編號去求模得到的是0-99之間的數(shù)據(jù),這一百個表編號為0-99,分別對應(yīng)存儲即可,無論是自動增長還是非自動增長也不太可能落在同一個表上面去;而對于某些熱點用戶,如果按照用戶拆分,這些熱點用戶的訪問就會被反復(fù)訪問到同一個表,比如類似微博這種東西,也許某個熱門人物的他的好友個數(shù)就會非常多,可能會導(dǎo)致某個表非常大,所以為了緩解這種問題,我們會再做二次hash;而對于一些非數(shù)字類的數(shù)據(jù),我們一般會采取對其前幾個字符的ascii或者h(yuǎn)ash值等取出來做操作,具體看實際情況而定;那么hash算法就是完美的嗎?當(dāng)然不是,它最痛苦的就是拆分,一旦拆分將會面臨各種問題,應(yīng)用要重啟,配置要修改,數(shù)據(jù)要遷移;雖然用了一些手段來解決,但是這些手段一般都是需要提前預(yù)估出來的,比如hash算法一般都是2的次方拆分法則,因為數(shù)據(jù)庫都會有備庫,而且很多時候會有多個備庫,所以如果做2倍數(shù)拆分的時候,可以直接將一個備份庫上的數(shù)據(jù)拿上來用,如原來拆分規(guī)則為100,現(xiàn)在變成200,按照100求模=1的機器的主庫數(shù)據(jù)和200求模等于1的都還在這個上面,只是有一部分和200求模會變成101(理論上可以認(rèn)為是一半),備庫也是這樣,所以在乘以2以后這個備庫變成主庫后,數(shù)據(jù)是完全可以路由到的,剩余的工作就只需要將原有主庫上和200求模等于101的數(shù)據(jù)刪掉,以將這部分空間節(jié)約出來,而原有備庫替換成的主庫上和200求模等于1的數(shù)據(jù)刪掉,刪掉的這個過程是痛苦的,時間長,資源多,全是IO操作,而且還有各種鎖,性能影響極大;試圖想到我可以直接把他干掉,幾乎不影響性能就除非需要刪掉的數(shù)據(jù)是一個獨立的邏輯單位,在同一個表上能想到的就是分區(qū),也就是它如果是一個分區(qū)你就可以直接把他很快的drop掉或truncate掉,這種必須要有提前的預(yù)案才可以,否則這些都是空想;所以這種基于hash的拆分一般不要隨便拆分,代價是很大的,因為這個上面的每個節(jié)點都需要做切割,甚至于只有一個備庫的需要做遷移,要盡量少做,壓力來了也得做,所以需要預(yù)估未來幾年幾乎不太可能做這樣的事情,這個估算是根據(jù)業(yè)務(wù)的發(fā)展速度和趨勢來決定的。

剩下是一種很常規(guī)但是很少用到的拆分,就是基于位圖的拆分,也就是認(rèn)為的講某個字段(這個字段的值是可以被列舉的),某些值放在某個放在某個表里面,也就是表的個數(shù)是被定義好的,拆分的個數(shù)收到值的個數(shù)的限制,除非和其他字段再進行二次組合;雖然它本身用途不多,但是如果以range或hash作為前提它也有可能是有用途的。

上面闡述了幾種基本的拆分方法,都有各自有優(yōu)缺點,為了更好的解決問題,我們考慮得失,會考慮使用他們進行組合,組合的方法根據(jù)實際情況而定,如我們在一些數(shù)字列上,既想考慮擴展性,又想考慮負(fù)載均衡,那么在可接收的條件下,那么我們將range-hash或hash-range,至于是那一種要看具體情況,我們簡單說下range-hash,它在做range的時候,每個hash值就面對多個主機目標(biāo),在這部分主機目標(biāo)內(nèi)部做相應(yīng)的hash負(fù)載均衡,如果出現(xiàn)熱點,在這個range內(nèi)部做二次拆分,其他的range是不需要的,如果負(fù)載較低,可以合并一些數(shù)據(jù),range拆分的條件只是負(fù)責(zé)某個數(shù)據(jù)段的數(shù)據(jù)太多,較為均衡的分布數(shù)據(jù),多個range如果以后不是怎么用了,可以將多個range的數(shù)據(jù)進行再次合并(這個代價相對較大,因為每個range下面的hash規(guī)則可以是不一樣的,但是如果只要2的多少次方來完成這個動作,就不會出現(xiàn)太大的問題);而面對字符串的數(shù)據(jù),或者不是自動增長類的數(shù)據(jù),range沒有辦法,因為范圍不可預(yù)知,雖然可以通過ascii來取,我們的范圍也可以用正無窮和負(fù)無窮來代表,但是我們無法保證數(shù)據(jù)的均衡的,所以建議還是先做hash,而在拆分的過程中,為了使得應(yīng)用不停需要設(shè)置一個版本號,也就是拆分過程中,的時間戳標(biāo)記,所有在這個時間點以后的數(shù)據(jù)都在新的分布式規(guī)則中,老的數(shù)據(jù)讀取的時候在老的規(guī)則中,然后可以遷移數(shù)據(jù),但是遷移過程中性能是很低的,遷移完成后就將中間規(guī)則去掉就完成了整個的拆分過程,這個拆分過程就不局限于必須是2倍拆分了。

有關(guān)組合條件有很多,可以根據(jù)自己的應(yīng)用場景去選取不同的組合方法,使得它的性能最佳,盡量少出現(xiàn)跨庫跨表的操作,如果是按照非拆分字段進行查詢,要么做二級拆分,要么就是做索引表,索引表也可以是拆分的表,也就是先查索引表然后再從索引表得到的主表的分表字段去找主表內(nèi)容(但是由于索引表的結(jié)構(gòu)完全又開發(fā)人員自己定義,所以索引表的維護完全是程序來控制,一致性需要開發(fā)人員來保證)。

如上,拆分解決了很多問題,也帶來了很多新問題,如維護成本極度上升,需要大量外圍軟件來支持,否則發(fā)生任何問題將無從下手;其二,開發(fā)人員要編寫很多的代碼來處理路由規(guī)則信息和分布式的一致性數(shù)據(jù)的問題;切分和數(shù)據(jù)庫切換過程中,一次要切換一大堆機器,應(yīng)用重啟時間很長;動態(tài)擴展要實現(xiàn)就需要非常復(fù)雜的代價。

為了解決第一個問題,公司需要較好的基層架構(gòu)的人員,來編寫很多外圍的類似分布式一致性監(jiān)控、問題跟蹤處理等工具軟件,并且這些軟件要可持續(xù)的,否則經(jīng)常換成本永遠(yuǎn)無法控制,只要基層做好了,以后這些成本就會越來越少了,或者這些成本在同等的業(yè)務(wù)水平下會越來越少。

為了解決第二個問題,讓開發(fā)來編寫路由等信息肯定是不合理的,一個是很多開發(fā)人員水平有限,數(shù)據(jù)是業(yè)務(wù)關(guān)鍵,路由更加保護這數(shù)據(jù)存儲在哪里,所以要是代碼寫得不好就死定了;于是我們需要獨立中間件,這個中間件可以只是保留在應(yīng)用中的一個算法,也可以是一個獨立的服務(wù)模式,服務(wù)模式為了保證其不是單點問題以及訪問壓力過大,需要優(yōu)化的是提供服務(wù)應(yīng)當(dāng)是一個集群,而所有訪問它的應(yīng)用系統(tǒng)應(yīng)當(dāng)做一定算法的本地緩存來處理;這又回到我們上一章節(jié)說的應(yīng)用系統(tǒng)的拆分了。

為了解決第三個問題,切換要讓應(yīng)用不知道,那么就要讓應(yīng)用感覺不到IP、PORT、庫名稱的變化,那么就必須將這些東西抽象出來,抽象為如上所示的獨立服務(wù)模式或本地配置,當(dāng)一個統(tǒng)一的配置被修改后,將會通知相關(guān)的應(yīng)用系統(tǒng)進行修改,并一致性將多套機器全部一次性切換完成。

為了解決第四個問題,我們想了很多拆分的動態(tài)擴展性,但是算法十分復(fù)雜,就增加第二個中間件的復(fù)雜性,并且面臨各種風(fēng)險,所以傳統(tǒng)RDBMS的拆分再次受到水平擴展的限制,人為介入太多,主要原因就是先獨立做數(shù)據(jù)庫,再做上層管理,是一個從下到上的過程,也就是有問題貼補丁的過程,而并非從一個站在高處把控一切的思想;于是為了解決更多的特殊的問題,如數(shù)據(jù)量超級大,而且增量也很多,讀的訪問非常多的情況,nosql這種低耦合的拆分技術(shù)出現(xiàn)了,也是云計算來臨的基礎(chǔ)(現(xiàn)在云計算這個詞匯徹底被用亂了,尤其是中國,太過分了,這么好歌東西,在國內(nèi)就被到處使用,用著當(dāng)招牌,對此我表示無語),關(guān)于這部分不是本文的重點,最后一章節(jié)會簡單提及一些bigtable的思路和原理,以及其開源版本的實現(xiàn)HBase的大概的架構(gòu)模式。

Nosql技術(shù)概述:

谷歌是一家偉大的互聯(lián)網(wǎng)公司,其引領(lǐng)著互聯(lián)網(wǎng)時代的發(fā)展,bigtable的經(jīng)典一直在世界各大互聯(lián)網(wǎng)公司所效仿,后來還有多個升級版本,但是大家還是喜歡叫他bigtable;谷歌取名字很奇怪,就是直截了當(dāng),bigtable就是大表,什么樣的表的是大表,每個幾十億、幾百億、幾千億什么的,不是大表,谷歌的架構(gòu)可以承受萬億級別的數(shù)量,它的MapReduce也是一個非常簡單的名詞,也就是先做Map處理(也就是將需要分析的目標(biāo)中將需要分析的有效數(shù)據(jù)提取出來并組織為K-V結(jié)構(gòu)),而Reduce就負(fù)責(zé)將這些K-V數(shù)據(jù)進行處理;Apache也是一家偉大的公司,開源社區(qū)的大拿,在java界更加孕育了非常多的經(jīng)典,它的Hadoop架構(gòu)就是仿照谷歌的架構(gòu)來完成的,這套架構(gòu)完全是java編寫的,這個公司也有自己的特點,就是很多名字都是動物的名字,hadoop號稱就是大象的崛起,呵呵,這個hadoop架構(gòu)里頭有什么:pig、zookeeper就是什么豬、公園什么的意思,整個就是動物園,他們不需要多么玄的名字,就是純屬喜歡什么就用用什么,甚至于某些食物的名字或某個親人的名字。

Hadoop雖然還不可以和谷歌的架構(gòu)抗衡(基本差一個到兩個數(shù)量級),但是對于絕大部分的應(yīng)用是絕對沒有問題,在分布式運算上,它的MapReduce架構(gòu)可以支撐4000臺(在雅虎)的機器同時進行分布式運算;對于線上應(yīng)用就是其子模塊的HBase架構(gòu),全世界最大的是960臺(也是在雅虎)。

HBase算是nosql中的一種,每一種nosql都是為了解決某些特殊的問題而存在,因為前面看到分布式的拆分方法有很多種,nosql也不可能解決所有的問題,HBase總體來講需要配合的子系統(tǒng)有多個Region Server、Zookeeper、Master、HDFS來配合,其中HDFS為存儲引擎,所有和hadoop相關(guān)的內(nèi)容不論是不是HBase都基本是存在這個上面的,少部分內(nèi)容是存儲在本地文件(如MapReduce中Map后的中間結(jié)果可能會用本地文件來存儲,因為用完后中間數(shù)據(jù)就沒有用了,沒有必要放在HDFS上面);Zookeeper就是公園,管理這些動物,哪里出事或者門票都是它說了算,在這里如果誰宕機了(那個Region server宕機了),它要知道,然后告訴Master(管理者)去處理這些問題,Master宕機了就會啟動備用的Master,客戶端要請求首先就就是從Zookeeper請求,Zookeeper從Master哪里得到數(shù)據(jù)分布信息后,反饋給客戶端,客戶端會緩存主分布信息,然后開始訪問,如果訪問不到就再次請求Zookeeper,所以Zookeeper必須是多臺機器才能保證穩(wěn)定性。

也就是客戶端最終訪問的Region Server(其實這里可以看得出來它是基于范圍,但是這個范圍絕對不是基于某個自動增長列什么的,而是基于數(shù)據(jù)的字節(jié)碼匹配,可以放中文、數(shù)字什么的都可以,但是放進去前都需要轉(zhuǎn)換為二進制,所以轉(zhuǎn)換的過程完全是業(yè)務(wù)層自己定義的),這個東西你就可以理解為一個JVM節(jié)點,數(shù)據(jù)cache在內(nèi)存的是memstore,內(nèi)部存儲很多storefile,以hfile為單位,對文件進行讀寫;memstore一般都是64M兩個之間來回寫,一個寫滿就flush,另一個也在寫,如果另一個寫滿了,這個flush還未完成,就要鎖住兩個部分。

Region Server負(fù)責(zé)和HDFS通信,其另一個需要做的就是HLog的編寫,Hlog一般是數(shù)據(jù)實時寫的,但是也可以不是實時寫的;HBase數(shù)據(jù)的版本個數(shù)等方面都是可以設(shè)置的,并且可以保證單個操作的一致性;Master在初始化的時候,就會從HDFS上去獲取字典Meta信息,所以這些內(nèi)容都是存儲在HDFS上的。

OK,這部分并不是本文的重點,本文重點在于拆分,這里攜帶闡述了下HBase,但是它也有很多問題,相信問題最多的就是他是用java做的,對于后端的實時應(yīng)用一旦發(fā)生GC就有很多的問題,尤其是我們前面也簡單說了下GC對于這種半長命的鬼東西是很有問題的;其次是雖然ZK可以發(fā)現(xiàn)宕機,但是時間很長,這個心跳時間設(shè)置太短可能會是一種假死,心跳時間太長就宕機很多也不會被人發(fā)現(xiàn),總之還有很多問題,但是在JVM進步的同時,我們相信這些問題都可以得到解決,OK,本文就寫到這里,后續(xù)會參數(shù)拆分后各種問題的一些常見的解決方法。


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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多