細(xì)節(jié)優(yōu)化提升資源利用率
Author: 放翁(文初)
Email: fangweng@taobao.com
Mblog:weibo.com/fangweng
這里通過介紹對(duì)于淘寶開放平臺(tái)基礎(chǔ)設(shè)置之一的TOPAnalyzer的代碼優(yōu)化,來談一下對(duì)于海量數(shù)據(jù)處理的Java應(yīng)用可以共享的一些細(xì)節(jié)設(shè)計(jì)(一個(gè)系統(tǒng)能夠承受的處理量級(jí)別往往取決于細(xì)節(jié),一個(gè)系統(tǒng)能夠支持的業(yè)務(wù)形態(tài)往往取決于設(shè)計(jì)目標(biāo))。
先介紹一下整個(gè)TOPAnalyzer的背景,目標(biāo)和初始設(shè)計(jì),為后面的演變做一點(diǎn)鋪墊。
開放平臺(tái)從內(nèi)部開放到正式對(duì)外開放,逐步從每天幾千萬的服務(wù)調(diào)用量發(fā)展到了上億到現(xiàn)在的15億,開放的服務(wù)也從幾十個(gè)到了幾百個(gè),應(yīng)用接入從幾百個(gè)增加到了幾十萬個(gè)。此時(shí),對(duì)于原始服務(wù)訪問數(shù)據(jù)的分析需求就凸現(xiàn)出來:
1.
應(yīng)用維度分析(應(yīng)用的正常業(yè)務(wù)調(diào)用行為和異常調(diào)用行為分析)
2.
服務(wù)維度分析(服務(wù)RT,總量,成功失敗率,業(yè)務(wù)錯(cuò)誤及子錯(cuò)誤等)
3.
平臺(tái)維度分析(平臺(tái)消耗時(shí)間,平臺(tái)授權(quán)等業(yè)務(wù)統(tǒng)計(jì)分析,平臺(tái)錯(cuò)誤分析,平臺(tái)系統(tǒng)健康指標(biāo)分析等)
4.
業(yè)務(wù)維度分析(用戶,應(yīng)用,服務(wù)之間關(guān)系分析,應(yīng)用歸類分析,服務(wù)歸類分析等)
上面只是一部分,從上面的需求來看需要一個(gè)系統(tǒng)能夠靈活的運(yùn)行期配置分析策略,對(duì)海量數(shù)據(jù)作即時(shí)分析,將接過用于告警,監(jiān)控,業(yè)務(wù)分析。
下圖是最原始的設(shè)計(jì)圖,很簡單,但還是有些想法在里面:
Master:管理任務(wù)(分析任務(wù)),合并結(jié)果(Reduce),輸出結(jié)果(全量統(tǒng)計(jì),增量片段統(tǒng)計(jì))
Slave:Require Job + Do Job + Return Result,隨意加入,退出集群。
Job:(Input + Analysis Rule + Output)的定義。
幾個(gè)設(shè)計(jì)點(diǎn):
1.
后臺(tái)系統(tǒng)任務(wù)分配:無負(fù)載分配算法,采用細(xì)化任務(wù)+工作者按需自取+粗暴簡單任務(wù)重置策略。
2.
Slave與Master采用單向通信,便于容量擴(kuò)充和縮減。
3.
Job自描述性,從任務(wù)數(shù)據(jù)來源,分析規(guī)則,結(jié)果輸出都定義在任務(wù)中,使得Slave適用與各種分析任務(wù),一個(gè)集群分析多種日志,多個(gè)集群共享Slave。
4.
數(shù)據(jù)存儲(chǔ)無業(yè)務(wù)性(意味著存儲(chǔ)的時(shí)候不定義任何業(yè)務(wù)含義),分析規(guī)則包含業(yè)務(wù)含義(在執(zhí)行分析的時(shí)候告知不同列是什么含義,怎么統(tǒng)計(jì)和計(jì)算),優(yōu)勢在于可擴(kuò)展,劣勢在于全量掃描日志(無預(yù)先索引定義)。
5.
透明化整個(gè)集群運(yùn)行狀況,保證簡單粗暴的方式下能夠快速定位出節(jié)點(diǎn)問題或者任務(wù)問題。(雖然沒有心跳,但是每個(gè)節(jié)點(diǎn)的工作都會(huì)輸出信息,通過外部收集方式快速定位問題,防止集群為了監(jiān)控耦合不利于擴(kuò)展)
6.
Master單點(diǎn)采用冷備方式解決。單點(diǎn)不可怕,可怕的是丟失現(xiàn)場和重啟或重選Master周期長。因此采用分析數(shù)據(jù)和任務(wù)信息簡單周期性外部存儲(chǔ)的方式將現(xiàn)場保存與外部(信息盡量少,保證恢復(fù)時(shí)快速),另一方面采用外部系統(tǒng)通知方式修改Slave集群MasterIP,人工快速切換到冷備。
Master的生活軌跡:
Slave的生活軌跡:
有人會(huì)覺得這玩意兒簡單,系統(tǒng)就是簡單+透明才會(huì)高效,往往就是因?yàn)橄到y(tǒng)復(fù)雜才會(huì)帶來更多看似很高深的設(shè)計(jì),最終無非是折騰了自己,苦了一線。廢話不多說,背景介紹完了,開始講具體的演變過程。
數(shù)據(jù)量:2千萬 à 1億 à 8億 à15億。報(bào)表輸出結(jié)果:10份配置à30份à60份à100份。統(tǒng)計(jì)后的數(shù)據(jù)量:10k à 10M à 9G。統(tǒng)計(jì)周期的要求:1天à5分鐘à3分鐘à1分半。
從上面這些數(shù)據(jù)可以知道從網(wǎng)絡(luò)和磁盤IO,到內(nèi)存,到CPU都會(huì)經(jīng)歷很大的考驗(yàn),由于Master是縱向擴(kuò)展的,因此優(yōu)化Master成為每個(gè)數(shù)據(jù)跳動(dòng)的必然要求。由于是用Java寫的,因此內(nèi)存對(duì)于整體分析的影響更加嚴(yán)重,GC的停頓直接可以使得系統(tǒng)掛掉(因?yàn)閿?shù)據(jù)在不斷流入內(nèi)存)。
優(yōu)化過程:
縱向系統(tǒng)的工作的分擔(dān):
從Master的生活軌跡可以看到,它負(fù)荷最大的一步就是要去負(fù)責(zé)Reduce,無論如何都需要交給一個(gè)單節(jié)點(diǎn)來完成所有的Reduce,但并不表示對(duì)于多個(gè)Slave的所有的Reduce都需要Master來做。有同學(xué)給過建議說讓Master再去分配給不同的Slave去做Slave之間的Reduce,但一旦引入Master對(duì)Slave的通信和管理,這就回到了復(fù)雜的老路。因此這里用最簡單的方式,一個(gè)機(jī)器可以部署多個(gè)Slave,一個(gè)Slave可以一次獲取多個(gè)Job,執(zhí)行完畢后本地合并再匯報(bào)給Master。(優(yōu)勢:Master在Job合并所產(chǎn)生的內(nèi)存消耗可以減輕,因?yàn)檫@是統(tǒng)計(jì),所以合并后數(shù)據(jù)量一定大幅下降,此時(shí)Master合并越少的Job數(shù)量,內(nèi)存消耗越小),因此Slave的生活軌跡變化了一點(diǎn):
流程中間數(shù)據(jù)優(yōu)化:
這里舉兩個(gè)例子來說明對(duì)于處理中中間數(shù)據(jù)優(yōu)化的意義。
在統(tǒng)計(jì)分析中往往會(huì)有對(duì)分析后的數(shù)據(jù)做再次處理的需求,例如一個(gè)API報(bào)表里面會(huì)有API訪問總量,API訪問成功數(shù),同時(shí)會(huì)要有API的成功率,這個(gè)數(shù)據(jù)最早設(shè)計(jì)的時(shí)候和普通的MapReduce字段一樣處理,計(jì)算和存儲(chǔ)在每一行數(shù)據(jù)分析的時(shí)候都做,但其實(shí)這類數(shù)據(jù)只有在最后輸出的時(shí)候才有統(tǒng)計(jì)和存儲(chǔ)價(jià)值,因?yàn)檫@些數(shù)據(jù)都可以通過已有數(shù)據(jù)計(jì)算得到,而中間反復(fù)做計(jì)算在存儲(chǔ)和計(jì)算上都是一種浪費(fèi),因此對(duì)于這種特殊的Lazy處理字段,中間不計(jì)算也不存儲(chǔ),在周期輸出時(shí)做一次分析,降低了計(jì)算和存儲(chǔ)的壓力。
對(duì)于MapReduce中的Key存儲(chǔ)的壓縮。由于很多統(tǒng)計(jì)的Key是很多業(yè)務(wù)數(shù)據(jù)的組合,例如APPAPIUser的統(tǒng)計(jì)報(bào)表,它的Key就是三個(gè)字段的串聯(lián):taobao.user.get—12132342—fangweng,這時(shí)候大量的Key會(huì)占用內(nèi)存,而Key的目的就是產(chǎn)生這個(gè)業(yè)務(wù)統(tǒng)計(jì)中的唯一標(biāo)識(shí),因此考慮這些API的名稱等等是否可以替換成唯一的短內(nèi)容就可以減少內(nèi)存占用。過程中就不多說了,最后在分析器里面實(shí)現(xiàn)了兩種策略:
1.
不可逆數(shù)字摘要采樣。
有點(diǎn)類似與短連接轉(zhuǎn)換的方式,對(duì)數(shù)據(jù)做Md5數(shù)字摘要,獲得16個(gè)byte,然后根據(jù)壓縮配置來采樣16個(gè)byte部分,用可見字符定義出64進(jìn)制來標(biāo)識(shí)這些采樣,最后形成較短的字符串。
由于Slave是數(shù)據(jù)分析者,因此用Slave的CPU來換Master的內(nèi)存,將中間結(jié)果用不可逆的短字符串方式表示。弱點(diǎn):當(dāng)最后分析出來的數(shù)據(jù)量越大,采樣md5后的數(shù)據(jù)越少,越容易產(chǎn)生沖突,導(dǎo)致統(tǒng)計(jì)不準(zhǔn)確。
2.
提供需要壓縮的業(yè)務(wù)數(shù)據(jù)列表。
業(yè)務(wù)方提供日志中需要替換的列定義及一組定義內(nèi)容。簡單來說,當(dāng)日志某一列可以被枚舉,那么就意味者這一列可以被簡單的替換成短標(biāo)識(shí)。例如配置APIName這列在分析生成key的時(shí)候可以被替換,并且提供了500多個(gè)api的名稱文件載入到內(nèi)存中,那么每次api在生成key的時(shí)候就會(huì)被替換掉名稱組合在key中,大大縮短key。那為什么要提供這些api的名稱呢?首先分析生成key在Slave,是分布式的,如果采用自學(xué)習(xí)的模式,勢必要引入集中式唯一索引生成器,其次還要做好足夠的并發(fā)控制,另一方面也會(huì)由并發(fā)控制帶來性能損耗。這種模式雖然很原始,但不會(huì)影響統(tǒng)計(jì)結(jié)果的準(zhǔn)確性,因此在分析器中被使用,這個(gè)列表會(huì)隨著任務(wù)規(guī)則每次發(fā)送到Slave中,保證所有節(jié)點(diǎn)分析結(jié)果的一致性。
特殊化處理特殊的流程:
在Master的生活軌跡中可以看出,影響一輪輸出時(shí)間和內(nèi)存使用的包括分析合并數(shù)據(jù)結(jié)果,導(dǎo)出報(bào)表和導(dǎo)出中間結(jié)果。在數(shù)據(jù)上升到1億的時(shí)候,Slave和Master之間數(shù)據(jù)通信以及Master的中間結(jié)果磁盤化的過程中都采用了壓縮的方式來減少數(shù)據(jù)交互對(duì)IO緩沖的影響,但一直考慮是否還可以再壓榨一點(diǎn)。首先導(dǎo)出中間結(jié)果的時(shí)候最初采用簡單的Object序列化導(dǎo)出,從內(nèi)存使用,外部數(shù)據(jù)大小,輸出時(shí)間上來說都有不少的消耗,仔細(xì)看了一下中間結(jié)果是Map<String,Map<String,Obj>>,其實(shí)最后一個(gè)Obj無非只有兩種類型Double和String,既然這樣,序列化完全可以簡單來作,因此直接很簡單的實(shí)現(xiàn)了類似Json簡化版的序列化,從序列化速度,內(nèi)存占用減少上,外部磁盤存儲(chǔ)都有了極大的提高,外部磁盤存儲(chǔ)越小,所消耗的IO和過程中需要的臨時(shí)內(nèi)存都會(huì)下降,序列化速度加快,那么內(nèi)存中的數(shù)據(jù)就會(huì)被盡快釋放。總體上來說就是特殊化處理了可以特殊化對(duì)待的流程,提高了資源利用率。(同時(shí)中間結(jié)果在前期優(yōu)化階段的作用就是為了備份,因此不需要每個(gè)周期都做,當(dāng)時(shí)做成可配置的周期值輸出)
再接著來談一下中間結(jié)果合并時(shí)候?qū)τ趦?nèi)存使用的優(yōu)化。Master會(huì)從多個(gè)Slave得到多個(gè)Map<Key,Map<Key,Value>>,合并過程就是對(duì)多個(gè)Map將第一級(jí)Key相同的數(shù)據(jù)做整合,例如第一級(jí)Key的一個(gè)值是API訪問總量,那么它對(duì)應(yīng)的Map中就是不同的api名稱和總量的統(tǒng)計(jì),而多個(gè)Map直接合并就是將一級(jí)key(API訪問總量)下的Map數(shù)據(jù)合并起來(同樣的api總量相加最后保存一份)。最簡單的做法就是多個(gè)Map<Key,Map<Key,Value>>遞歸的來合并,但如果要節(jié)省內(nèi)存和計(jì)算可以有兩個(gè)小改進(jìn),首先選擇其中一個(gè)作為最終的結(jié)果集合(避免申請(qǐng)新空間,也避免輪詢這個(gè)Map的數(shù)據(jù)),其次每一次遞歸時(shí)候,將合并后的后面的Map中數(shù)據(jù)移出(減少后續(xù)無用的循環(huán)對(duì)比,同時(shí)也節(jié)省空間)。看似小改動(dòng),但效果很不錯(cuò)。
再談一下在輸出結(jié)果時(shí)候的內(nèi)存節(jié)省。在輸出結(jié)果的時(shí)候,是基于內(nèi)存中一份Map<Key,Map<Key,Value>>來構(gòu)建的。其實(shí)將傳統(tǒng)的MapReduce的KV結(jié)果如何轉(zhuǎn)換成為傳統(tǒng)的Report,只需要看看Sql中的Group設(shè)計(jì),將多個(gè)KV通過Group by key,就可以得到傳統(tǒng)意義上的Key,Value,Value,Value。例如:KV可以是<apiName,apiTotalCount>,<apiName,apiResponse>,<apiName,apiFailCount>,如果Group by apiName,那么就可以得到 apiName,apiTotalCount,apiResponse,apiFailCount的報(bào)表行結(jié)果。這種歸總的方式可以類似填字游戲,因?yàn)槲覀兘Y(jié)果是KV,所以這個(gè)填字游戲默認(rèn)從列開始填寫,遍歷所有的KV以后就可以完整的得到一個(gè)大的矩陣并按照行輸出,但代價(jià)是KV沒有遍歷完成以前,無法輸出。因此考慮是否可以按照行來填寫,然后每一行填寫完畢之后直接輸出,節(jié)省申請(qǐng)內(nèi)存。按行填寫最大的問題就是如何在對(duì)KV中已經(jīng)處理過的數(shù)據(jù)打上標(biāo)識(shí),不要重復(fù)處理。(一種方式引入外部存儲(chǔ)來標(biāo)識(shí)這個(gè)值已經(jīng)被處理過,因?yàn)檫@些KV不可以類似合并的時(shí)候刪除,后續(xù)還會(huì)繼續(xù)要用,另一種方式就是完全備份一份數(shù)據(jù),合并完成后就刪除),但本來就是為了節(jié)約內(nèi)存的,引入更多的存儲(chǔ),就和目標(biāo)有悖了。因此做了一個(gè)計(jì)算換存儲(chǔ)的做法,例如填充時(shí)輪訓(xùn)的順序?yàn)椋?/span>K1V1,K2V2,K3V3,到K2V2遍歷的時(shí)候,判斷是否要處理當(dāng)前這個(gè)數(shù)據(jù),就只要判斷這個(gè)K是否在K1里面出現(xiàn)過,而到K3V3遍歷的時(shí)候,判斷是否要處理,就輪詢K1K2是否存在這個(gè)K,由于都是Map結(jié)構(gòu),因此這種查找的消耗很小,由此改為行填寫,逐行輸出。
最后再談一下最重頭的優(yōu)化,合并調(diào)度及磁盤內(nèi)存互換的優(yōu)化
從Master的生活軌跡可以看到,原來的主線程負(fù)責(zé)檢查外部分析數(shù)據(jù)結(jié)果狀態(tài),合并數(shù)據(jù)結(jié)果這個(gè)循環(huán),考慮到最終合并后數(shù)據(jù)只有一個(gè)主干,因此采用單線程合并模式來運(yùn)作,見下圖:
這張圖大致描述了一下處理流程,Slave隨時(shí)都會(huì)將分析后的結(jié)果掛到結(jié)果緩沖隊(duì)列上,然后主線程負(fù)責(zé)批量獲取結(jié)果并且合并。雖然是批量獲取,但是為了節(jié)省內(nèi)存,也不能等待太久,因?yàn)槊恳稽c(diǎn)等待就意味著大量沒有合并的數(shù)據(jù)將會(huì)存在與內(nèi)存中,但合并的太頻繁也會(huì)導(dǎo)致在合并過程中,新加入的結(jié)果會(huì)等待很久,導(dǎo)致內(nèi)存吃緊。或許這個(gè)時(shí)候會(huì)考慮,為什么不直接用多線程來合并,的確,多線程合并并非不可行,但要考慮如何兼顧到主干合并的并發(fā)控制,因?yàn)槎鄠€(gè)線程不可能同時(shí)都合并到數(shù)據(jù)主干上,由此引入了下面的設(shè)計(jì)實(shí)現(xiàn),半并行模式的合并:
從上圖可以發(fā)現(xiàn)增加了兩個(gè)角色:Merge Worker Thread Pool和Branch merged ResultList,與上面設(shè)計(jì)的差別就在于主線程不再負(fù)責(zé)合并數(shù)據(jù),而是批量的獲取數(shù)據(jù)交給合并線程池來合并,而合并線程池中的工作者在合并的過程中會(huì)競爭主干合并鎖,成功獲得的就和主干合并,不成功的就將結(jié)果合并后放到分支合并隊(duì)列上,等待下次合并時(shí)被主干合并或者分支合并獲得再次合并。這樣改進(jìn)后,發(fā)現(xiàn)由于數(shù)據(jù)掛在隊(duì)列沒有得到及時(shí)處理產(chǎn)生的內(nèi)存壓力大大下降,同時(shí)也充分利用了多核,多線程榨干了多核的計(jì)算能力(線程池大小根據(jù)cpu核來設(shè)置的小一點(diǎn),預(yù)留一點(diǎn)給GC用)。這種設(shè)計(jì)中還多了一些小的調(diào)優(yōu)配置,例如是否允許被合并過的數(shù)據(jù)多次被再次合并(防止無畏的計(jì)算消耗),每次并行合并最小結(jié)果數(shù)是多少,等待堆積到最小結(jié)果數(shù)的最大時(shí)間等等。(有興趣看代碼)
至上面的優(yōu)化為止,感覺合并這塊已經(jīng)被榨干了,但分析日志數(shù)據(jù)的增多,對(duì)及時(shí)性要求的加強(qiáng),使得我又要重新審視是否還有能力繼續(xù)榨出這個(gè)流程的水份。因此有了一個(gè)大膽的想法,磁盤換內(nèi)存。因?yàn)樵谡{(diào)度合并上已經(jīng)找不到更多可以優(yōu)化的點(diǎn)了,但是有一點(diǎn)還可以考慮,就是主干的那點(diǎn)數(shù)據(jù)是否要貫穿于整個(gè)合并周期,而且主干的數(shù)據(jù)隨著增量分析不斷增大(在最近這次優(yōu)化的過程中也就是發(fā)現(xiàn)GC的頻繁導(dǎo)致合并速度下降,合并速度下降導(dǎo)致內(nèi)存中臨時(shí)數(shù)據(jù)保存的時(shí)間久,反過來又影響GC,最后變成了惡性循環(huán))。盡管覺得靠譜,但不測試不得而知。于是得到了以下的設(shè)計(jì)和實(shí)現(xiàn):
這個(gè)流程發(fā)現(xiàn)和第二個(gè)流程就多了最后兩個(gè)步驟,判斷是否是最后的一次合并,如果是載入磁盤數(shù)據(jù),然后合并,合并完后將主干輸出到磁盤,清空主干內(nèi)存。(此時(shí)發(fā)現(xiàn)導(dǎo)出中間結(jié)果原來不是每次必須的,但是這種模式下卻成為每次必須的了)
這個(gè)改動(dòng)的優(yōu)勢在什么地方?例如一個(gè)分析周期是2分鐘,那么在2分鐘內(nèi),主干龐大的數(shù)據(jù)被外置到磁盤,內(nèi)存大量空閑,極大提高了當(dāng)前時(shí)間片結(jié)果合并的效率(GC少了)。缺點(diǎn)是什么?會(huì)在每個(gè)周期產(chǎn)生兩次磁盤大量的讀寫,但配合上優(yōu)化過的中間結(jié)果載入載出(前面的私有序列化)會(huì)適當(dāng)緩和。
由于線下無法模擬,就嘗試著線上測試,發(fā)現(xiàn)GC減少,合并過程加速達(dá)到預(yù)期,但是每輪的磁盤和內(nèi)存的換入換出由于也記入在一輪分析時(shí)間之內(nèi),每輪寫出最大時(shí)候70m數(shù)據(jù),需要消耗10多秒,甚至20秒,讀入最大需要10s,這個(gè)時(shí)間如果算在要求一輪兩分鐘內(nèi),那也是不可接受的),重新審視是否有疏漏的細(xì)節(jié)。首先載入是否可以異步,如果可以異步,而不是在最后一輪才載入,那么就不會(huì)納入到分析周期中,因此配置了一個(gè)可以調(diào)整的比例值,當(dāng)任務(wù)完成到達(dá)或者超過這個(gè)比例值的時(shí)候,將開始并行載入數(shù)據(jù),最后一輪等到異步載入后開始分析,發(fā)現(xiàn)果然可行,因此這個(gè)時(shí)間被排除在周期之外(雖然也帶來了一點(diǎn)內(nèi)存消耗)。然后再考慮輸出是否可以異步,以前輸出不可以異步的原因是這份數(shù)據(jù)是下一輪分析的主干,如果異步輸出,下一輪數(shù)據(jù)開始處理,很難保證下一輪的第一個(gè)任務(wù)是否會(huì)引發(fā)數(shù)據(jù)修改,導(dǎo)致并發(fā)問題,所以一直鎖定主干輸出,直到完成再開始,但現(xiàn)在每次合并都是空主干開始的,因此輸出完全可以異步,主干可以立刻清空,進(jìn)入下一輪合并,只要在下一個(gè)周期開始載入主干前異步導(dǎo)出主干完成即可,這個(gè)時(shí)間是很長的,完全可以把控,因此輸出也可以變成異步,不納入分析周期。
至此完成了所有的優(yōu)化,分析器高峰期的指標(biāo)發(fā)生了改變:一輪分析從2分鐘左右降低到了1分10秒,JVM的O區(qū)在合并過程中從50-80的占用率下降到20-60的占用率,GC次數(shù)明顯大幅減少。
總結(jié):
1.
利用可橫向擴(kuò)展的系統(tǒng)來分擔(dān)縱向擴(kuò)展系統(tǒng)的工作。
2.
流程中中間數(shù)據(jù)的優(yōu)化處理。
3.
特殊化處理可以特殊處理的流程。
4.
從整體流程上考慮不同策略的消耗,提高整體處理能力。
5.
資源的快用快放,提高同一類資源利用率。
6.
不同階段不同資源的互換,提高不同資源的利用率。
其實(shí)很多細(xì)節(jié)也許看了代碼才會(huì)有更深的體會(huì),分析器只是一個(gè)典型的消耗性案例,每一點(diǎn)改進(jìn)都是在數(shù)據(jù)和業(yè)務(wù)驅(qū)動(dòng)下不斷的考驗(yàn)。例如縱向的Master也許真的有一天就到了它的極限,那么就交給Slave將數(shù)據(jù)產(chǎn)出到外部存儲(chǔ),交由其他系統(tǒng)或者另一個(gè)分析集群去做二次分析。對(duì)于海量數(shù)據(jù)的處理來說都需要經(jīng)歷初次篩選,再次分析,展示關(guān)聯(lián)幾個(gè)階段,Java的應(yīng)用擺脫不了內(nèi)存約束帶來對(duì)計(jì)算的影響,因此就要考慮好自己的頂在什么地方。但優(yōu)化一定是全局的,例如磁盤換內(nèi)存,磁盤帶來的消耗在總體上來說還是可以接受的化,那么就可以被采納(當(dāng)然如果用上SSD效果估計(jì)會(huì)更好)。
最后還是想說的是,很多事情是簡單做到復(fù)雜,復(fù)雜再回歸到簡單,對(duì)系統(tǒng)提出的挑戰(zhàn)就是如何能夠用最直接的方式簡單的搞定,而不是做一個(gè)臃腫依賴龐大的系統(tǒng),簡單才看的清楚,看的清楚才有機(jī)會(huì)不斷改進(jìn)。