閱讀259 返回首頁    go 阿裏雲 go 技術社區[雲棲]


係統架構-性能篇章2(係統拆分1)

係統為什麼拆分?

係統做大了,並發量無法扛得住,如何做?

業務做複雜了,單個應用中不能個性化,如何做?

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

。。。。。。

拆分、拆分、再拆分。

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

 

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

 

首先我們看看應用一般是如何拆分的:

1、應用一般的企業內部都是按照業務增長方式比較多,所以隨著業務的增加,將係統進行拆分的是比較多的,也就是將一個較大的係統拆分為多個小的係統。

 

2、在一些企業中,不願意將係統拆分為小係統(原因後麵說明),而是將所有的內容部署在一起,依賴於集群分發到多個節點上去做負載均衡,這樣來完成一種切割,前序兩種也就是應用係統級別的縱橫向切割。

 

3、 獨立工具、模塊、服務的獨立化和集群化,基於SOA服務的企業級應用,很多模塊經過抽象後,並非子係統,而是一個獨立的服務係統,不參與業務,隻參與一個技術級別的功能服務,如MQ、JMS、MemCached等,我們經常也管這一類叫做中間件,也就是平台沒有提供自己來做或第三方提供的中間件(當然中間 件也包含應用服務器)。

 

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

 

5、由於上述各類拆分導致的運維的困難,在數以萬計的計算機集群下,如何動態資源分配和拆分以及拋開分布式的內部細節來編程,如何自動化運維係統就是大型計算機集群下需要考慮的問題-雲存儲與雲計算。

 

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

1、負載均衡器的問題。

2、不同係統之間的通信問題。

3、數據寫入和查找的問題。

4、跨數據庫事務問題。

5、跨數據庫序列問題。

6、不同應用的本地緩存問題。

7、係統之間的直接依賴和間接依賴問題。

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

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

10、統一監控和恢複問題。

 

本 文我們暫時不討論關於雲存儲方麵的問題,先引入話題,不過每項技術的產生都是為了解決某些特定的問題而存在,所以雲也並非萬能的,後麵的文章我們會介紹一 些基於純java開發的hadoop相關架構和模塊(如:MapReduce、Hbase、Hive、HDFS、Pig等子係統,說明當今海量信息的互聯 網中大象的崛起)。

 

1、係統按照業務拆分

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

   a.隨著業務的發展,模塊之間的耦合性越來越強

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

   c.係統啟動加載PermGen的時間也會很長並且需要很大的PermGen,更加重要的原因是JVM在CMSGC出來之前管理大內存是有問題的

   d.尤其是發生Full GC時候在大內存的JVM上暫停時間是相當的長,長得讓人無法接受.

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

   f.一個大係統由多個開發商完成,多個開發商都有自己的主打產品,為自己節約成本,將各個產品以集成的方法完成一個大係統的業務過程。

  等等原因。

 

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

 

所以拆分應當具有的最基本條件是高內聚、 低耦合的條件,也就是說,這個係統和外部係統的調用模塊對於整個係統的模塊來講是比較少的,而不是大部分模塊都是在和外部係統交互,除了專門用於處理係統交互的係統外,這樣的拆分設計是肯定不合理的,因為通信的代價遠遠大於本地JVM的代價。

 

開 發人員越來越多,從最初的一個人,幾個人,到幾十人,幾百人甚至上千人,在一個工程中來寫代碼是很恐怖的事情,誰改了沒法查出來,無法定位,很亂,所以拆 分在一定程度上可以將版本控製的粒度細化一下,但是並不代表拆分後就沒有版本問題;隨著產品不斷模塊化和抽象化,在大多數的應用中,獨立的子係統就會成為一個獨立的行業產品,可以基於配置模式的適用於大部分的地區工廠或者企業的應用,也可以通過一個頂層版本分發出來的多個地區化個性化版本(可能有兩層結 構);也就是在節約大部分共享勞動力的基礎上如何做到個性化平台,這也是行業軟件中非常盛行的,不過這樣將絕大部分程序員控製在一個小匣子裏麵了,幾乎沒有發揮的空間。

 

上麵也說了,係統可能由幾十人、幾百人甚至於上千人去 寫,如果大家都寫一個工程,代碼量可想而知,係統初始化需要加載代碼段到PermGen,這塊空間將不可預知的大小發展,並且隨著業務的複雜性,需要的引 入的第三方技術越來越多,第三發包的class同樣會占用PermGen的空間,不用多說,這塊空間的大小是不可預知的。

 

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

 

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

 

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

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

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

2、係統水平拆分:係統水平拆分即同一個子係統,或者整個係統部署在多個node上,這些node可以是多個主機或同一個主機上的多個軟件節點;但是這些節點目前來講即使應用拆分得再細,在分布式係統上的這種低端機器也不可能扛得住高並發的訪問,一般這類低端服務器代碼調解得較好等各種情況下,服務器的QPS一般都是保持在200以內的(這是以16個CPU來處理,一個請求在80ms內處理完成請求分派,業務處理和數據請求,反饋結果等過程已經是非常快速的了,很多時候一個SQL都會超過這個時間),當然單用幾個字節去做壓力測試,反饋幾個字節,並且中間幾乎沒有IO方麵的額請求(如數據庫、文件、遠程方法調用等),那麼這個QPS你可能會上千,甚至於在好的機器上可以上萬也有可能。

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

這種拆分沒有什麼太高的要求,隻要負載均衡設備可以支撐就可以,為了讓負載均衡可以支撐更大的壓力,那麼就盡量讓節點數量減少,那麼就希望在同一台實體機器上盡量一個節點(通過對實體機器進行虛擬化可以在某些情況下節約成本,並將物理機本身的性能發揮到一個極限,並可以將一個比較好一點的機器分攤給多個訪問量較低的係統,不過虛擬化本身也會產生很多開銷,在這些方麵需要綜合權衡一下好壞),可惜目前來講Oracle的Hotspot VM還不足以支撐大型的非常好的實時係統(我們很多時候不得不在同一個大內存機器上部署多個小的JVM節點),尤其麵對幾種場景顯得力不從心:

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

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

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

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

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

3、 獨立工具、模塊、服務的獨立化和集群化

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

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

第一種和這種拆分方法沒有明顯的區別,可以說這種拆分的思想是受第一種拆分的影響或者說基於它來做的,它的目的是以一個個體或者集群為外部提供一種公共服務;當一個企業或者一個大的互聯網公司,將這些公共服務開放出來後,形成一種全局的數據、技術的服務平台。

4、數據庫拆分

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

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

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

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

在早期的數據庫拆分中,有主備讀寫分離,ORACLE RAC多實例運算,不過麵對越來越龐大的係統,他們都顯得力不從心了,當然讀寫分離還是可以和現有的人工拆分所兼容,人工拆分主要是為了得到更好的水平擴展。

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

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

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

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

有關組合條件有很多,可以根據自己的應用場景去選取不同的組合方法,使得它的性能最佳,盡量少出現跨庫跨表的操作,如果是按照非拆分字段進行查詢,要麼做二級拆分,要麼就是做索引表,索引表也可以是拆分的表,也就是先查索引表然後再從索引表得到的主表的分表字段去找主表內容(但是由於索引表的結構完全又開發人員自己定義,所以索引表的維護完全是程序來控製,一致性需要開發人員來保證)。

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

為了解決第一個問題,公司需要較好的基層架構的人員,來編寫很多外圍的類似分布式一致性監控、問題跟蹤處理等工具軟件,並且這些軟件要可持續的,否則經常換成本永遠無法控製,隻要基層做好了,以後這些成本就會越來越少了,或者這些成本在同等的業務水平下會越來越少。

為了解決第二個問題,讓開發來編寫路由等信息肯定是不合理的,一個是很多開發人員水平有限,數據是業務關鍵,路由更加保護這數據存儲在哪裏,所以要是代碼寫得不好就死定了;於是我們需要獨立中間件,這個中間件可以隻是保留在應用中的一個算法,也可以是一個獨立的服務模式,服務模式為了保證其不是單點問題以及訪問壓力過大,需要優化的是提供服務應當是一個集群,而所有訪問它的應用係統應當做一定算法的本地緩存來處理;這又回到我們上一章節說的應用係統的拆分了。

為了解決第三個問題,切換要讓應用不知道,那麼就要讓應用感覺不到IP、PORT、庫名稱的變化,那麼就必須將這些東西抽象出來,抽象為如上所示的獨立服務模式或本地配置,當一個統一的配置被修改後,將會通知相關的應用係統進行修改,並一致性將多套機器全部一次性切換完成。

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

Nosql技術概述:

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

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

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

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

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

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


最後更新:2017-04-02 06:51:56

  上一篇:go API Demos 2.3 學習筆記 (13)-- Views->Seek Bar
  下一篇:go 深入剖析Android應用開發--視頻