- 論壇徽章:
- 0
|
[color="#02368d"]Java EE 常見性能問題解決手冊(cè)(4)
經(jīng)自:http://blog.chinaunix.net/u/10516/showart_1003390.html
設(shè)置一個(gè)飽和的池,然后逐步減少連接池大小,一直到CPU占用率為75%到85%之間,同時(shí)用戶負(fù)載正常。如果等待隊(duì)列大小實(shí)在無法控制,考慮下面2中建議:
- 1.把你的程序放入代碼模擬器運(yùn)行,調(diào)整程序代碼
- 2.增加額外的硬件
如果你的用戶負(fù)載超過了環(huán)境能承受的范圍,你應(yīng)該考慮修正代碼減少和CPU的沖突或者增加CPU。
JDBC連接池
很多JAVA EE 程序連接到一個(gè)后臺(tái)數(shù)據(jù)源,大多數(shù)是通過JDBC(JAVA DATABASE
CONNECTIVITY)將程序和后臺(tái)連接起來。由于創(chuàng)建數(shù)據(jù)庫連接的代價(jià)很高,程序服務(wù)器讓在同一個(gè)程序服務(wù)器實(shí)例下的所有程序共享特定數(shù)量的一些連
接。如果一個(gè)請(qǐng)求需要連接到數(shù)據(jù)庫,但是數(shù)據(jù)庫的連接池?zé)o法為這個(gè)請(qǐng)求創(chuàng)建一個(gè)新連接,這個(gè)時(shí)候請(qǐng)求就會(huì)停下來等待連接池完成自己的操作再給她分配一個(gè)連
接。反過來,如果數(shù)據(jù)庫連接池太大程序服務(wù)器就會(huì)浪費(fèi)資源,并且程序有可能強(qiáng)迫數(shù)據(jù)庫承受過量的負(fù)荷。我們調(diào)試的目的就是盡量減少請(qǐng)求的等待時(shí)間和飽和的
資源之間之間的沖突,讓一個(gè)請(qǐng)求在數(shù)據(jù)庫外等待要比強(qiáng)迫數(shù)據(jù)庫好的多。
一個(gè)程序服務(wù)器如果設(shè)置連接的數(shù)量不合理就會(huì)有下面這些特征:
- 1.程序運(yùn)行速度緩慢
- 2.CPU使用率低
- 3.數(shù)據(jù)庫連接池使用率非常高
- 4.線程等待數(shù)據(jù)庫的連接
- 5.線程使用率很高
- 6.請(qǐng)求隊(duì)列中有待處理的請(qǐng)求(潛在的)
- 7.數(shù)據(jù)庫CPU使用率很低(因?yàn)闆]有足夠的請(qǐng)求能夠讓他繁忙起來)
JDBC prepared statements
和JDBC相關(guān)的另一個(gè)重要的設(shè)置就是:為JDBC使用的statement 所預(yù)設(shè)的緩存的大小。當(dāng)你的程序在數(shù)據(jù)庫中運(yùn)行SQL statement 的時(shí)候三下面3個(gè)步驟進(jìn)行:
- 1.準(zhǔn)備
- 2.執(zhí)行
- 3.返回?cái)?shù)值
在準(zhǔn)備階段,數(shù)據(jù)庫驅(qū)動(dòng)器讓數(shù)據(jù)庫完成隊(duì)列中的執(zhí)行計(jì)劃。執(zhí)行的時(shí)候,數(shù)據(jù)庫執(zhí)行語句并返回指向結(jié)果的引用。在返回的時(shí)候,程序重新描述這些結(jié)果并描述出這些被請(qǐng)求的信息。
數(shù)據(jù)庫驅(qū)動(dòng)會(huì)這樣優(yōu)化程序:首先,你需要去準(zhǔn)備一個(gè)statement ,這個(gè)statement 它會(huì)讓數(shù)據(jù)庫做好執(zhí)行和緩存結(jié)果的準(zhǔn)備。在此同時(shí),數(shù)據(jù)庫驅(qū)動(dòng)會(huì)從緩存中裝載已經(jīng)準(zhǔn)備好的statement ,而不用直接連接到數(shù)據(jù)庫。
如果prepared statement 設(shè)置太小,數(shù)據(jù)庫驅(qū)動(dòng)器會(huì)被迫去查詢沒有裝載進(jìn)緩存區(qū)的statement
,這就會(huì)增加額外的連接到數(shù)據(jù)庫的時(shí)間。prepared statement
緩存區(qū)設(shè)置不恰當(dāng)最主要的癥狀就是花費(fèi)大量的時(shí)間去連接相同的statement。這段被浪費(fèi)的時(shí)間本來是為了讓它去裝載后面的調(diào)用的。
事情變的稍微復(fù)雜了點(diǎn),緩存prepared statement
是每個(gè)statement的基礎(chǔ),就是說在一個(gè)statement連接之前都應(yīng)當(dāng)緩存起來。這個(gè)增加的復(fù)雜性就產(chǎn)生了一個(gè)沖突:如果你有100個(gè)
prepared statement需要去緩存,但你的連接池中有50個(gè)數(shù)據(jù)庫連接,這個(gè)時(shí)候你就需要有存放5000條預(yù)備語句的內(nèi)存。
通過跟蹤性能,確定出你程序所執(zhí)行的不重復(fù)的statement 的數(shù)量,并從這些statement 中找出哪些條是頻繁執(zhí)行的。
Entity bean(實(shí)體BEAN)和stateful session bean的緩沖
無狀態(tài)(stateless)對(duì)象可以被放入到池中共享,但象Entity beans和 stateful session
bean這樣的有狀態(tài)的對(duì)象就需要被緩存,因?yàn)檫@些bean的每個(gè)實(shí)例都是不相同的。當(dāng)你需要一個(gè)有狀態(tài)對(duì)象時(shí),你需要明確創(chuàng)建這個(gè)對(duì)象的特定實(shí)例,普通
的實(shí)例是不能滿足的。類似的,你考慮一個(gè)超市類似的情況,你需要個(gè)售貨員但他叫什么并不重要,任何售貨員都可以滿足你。也就是,售貨員被放入池中共享,因
為你只需要是售貨員就可以,而不是一個(gè)叫做史締夫的這個(gè)售貨員。當(dāng)你離開超市的時(shí)候,你需要帶上你的孩子,不是其他人的孩子,而是你自己的。這個(gè)時(shí)候,孩
子就需要被緩存。
![]()
Figure 10. The application requests an object from the cache that
is in the cache, so a reference to that object is returned without
making a network trip to the database
當(dāng)你的緩存區(qū)太小的時(shí)候,緩存的性能就會(huì)明顯的受到影響。特別是,當(dāng)一個(gè)請(qǐng)求去一個(gè)已經(jīng)滿了的緩存區(qū)域去請(qǐng)求一個(gè)對(duì)象的時(shí)候,下面的步驟就會(huì)執(zhí)行,這些步驟會(huì)在圖11中顯示:
- 1. 程序請(qǐng)求一個(gè)對(duì)象
- 2. 緩存檢測(cè)這個(gè)對(duì)象是否已經(jīng)存在于緩存中
- 3. 緩存決定把一個(gè)對(duì)象開除出緩存(一般采用的算法是遺棄最近使用次數(shù)最少的對(duì)象)
- 4. 把這個(gè)對(duì)象扔出緩存(稱為passivated)
- 5. 把從數(shù)據(jù)庫中裝載這個(gè)新對(duì)象并放入到緩存(稱為activated)
- 6. 把指向這個(gè)對(duì)象的引用返回給程序
![]()
Figure 11. Because the requested object is not in the cache, an
object must be selected for removal from the cache and removed from it.
如果多數(shù)的請(qǐng)求都需要執(zhí)行這些步驟的話,那你采用緩存技術(shù)就不是好的選擇了!如果這些處理步驟頻繁發(fā)生的話,你就需要重新推敲下你的緩存了;
憶一下:從緩存中去除一個(gè)對(duì)象稱為passivation,從持久存儲(chǔ)區(qū)取出一個(gè)對(duì)象放入緩存稱為activation。能在緩存中找到的請(qǐng)求(緩存中有
此請(qǐng)求的對(duì)象)的百分率稱為hit ratio,相反找不到的請(qǐng)求的百分率稱為miss ratio。
緩存剛被初始化的時(shí)候,hit
ratio是0,它的activation數(shù)量非常高,因此在初始化后你需要去觀察緩存的性能。初始化以后,你應(yīng)該跟蹤passivation的數(shù)量并把
它和與向緩存請(qǐng)求對(duì)象的請(qǐng)求的總量相比較,因?yàn)閜assivations只會(huì)發(fā)生在緩存被初始化以后。但一般來說,我們更需要關(guān)心緩存的miss
ratio。如果miss
ratio超過25%,那么緩存可能是太小了。因此,如果missratio的數(shù)量超過75%,那么不是你的緩存設(shè)置的太小就是你不需要緩存這個(gè)技術(shù)。
一旦你覺得你的緩存太小,就去嘗試著增大大小,并測(cè)試增加的性能。如果miss ration下降到20%以下,那你的緩存的大小就非常棒了,如果沒有什么效果,那么你就需要和這個(gè)程序的技術(shù)員聯(lián)系,看是這個(gè)對(duì)象是不是需要緩存或者是否應(yīng)該修正程序中這個(gè)對(duì)象的代碼。
Staless session bean和message-driven bean池
Stateless session bean 和message-driven bean
在商業(yè)應(yīng)用方面很重要,不要期望它們會(huì)保持自己特有的狀態(tài)信息。當(dāng)你的程序需要使用這些BEAN的商業(yè)功能的時(shí)候,它就從一個(gè)池中取出一個(gè)BEAN實(shí)例,
用這個(gè)實(shí)例來調(diào)用一個(gè)個(gè)方法,用完后再將BEAN的實(shí)例再放回到池中。如果你的程序過了一會(huì)又需要這個(gè)一摸一樣的BEAN,就從池中再得到一個(gè)實(shí)例,但不
能保證你得到的就是上一個(gè)實(shí)例。池能夠讓程序共享資源,但是會(huì)讓你的程序付出潛在的等待時(shí)間。如果你無法從池中得到想要的BEAN,請(qǐng)求就會(huì)等待,一直到
這個(gè)BEAN被放入到池中。很多程序服務(wù)器都會(huì)把這些池調(diào)整的很好,但是我碰到過因?yàn)樵诃h(huán)境中把他們?cè)O(shè)置的太小而引發(fā)的不少麻煩。Stateless
bean池的大小應(yīng)該和可執(zhí)行線程池的大小一般大,因?yàn)橐粋(gè)線程同時(shí)只能使用一個(gè)對(duì)象,再多了就造成浪費(fèi)的。因此,一些程序服務(wù)器把池的大小和線程的數(shù)量
設(shè)置成同樣的數(shù)量。為了保險(xiǎn)起見,你應(yīng)該親自把它設(shè)置成這個(gè)數(shù)。
事務(wù)
使用Enterprise Java的一個(gè)好處就是它天生就支持事務(wù)。通過JAVAEE 5 EJB(Enterprise javaBeans)的注釋,你可以控制事務(wù)中方法的使用。事務(wù)會(huì)以下面2中方式結(jié)束:
當(dāng)一個(gè)事務(wù)被提交的時(shí)候,說明它已經(jīng)完全成功了,但是當(dāng)它回滾的時(shí)候,就說明發(fā)生了一些錯(cuò)誤。回滾會(huì)是下面2種情況:
- 1. 程序造成的回滾(程序回滾)
- 2. 非程序造成的回滾(非程序回滾)
通常,程序回滾是因?yàn)樯虡I(yè)的規(guī)定。比如一個(gè)WEB程序做一個(gè)素描畫的價(jià)格的調(diào)查,程序可能讓用戶輸入年齡,并且商業(yè)規(guī)定18歲以上才可以進(jìn)入。
如果一個(gè)16歲的提交了信息,那么程序就會(huì)拋出一個(gè)錯(cuò)誤,打開一個(gè)網(wǎng)頁告訴他,他年齡還不能參與到這個(gè)信息的調(diào)查。因?yàn)槌绦驋伋隽水惓,因此包含在程序?br />
的事務(wù)的就會(huì)發(fā)生回滾。這只是普通的程序回滾,只有當(dāng)發(fā)生大量的程序回滾才值得我們注意。
另一方面,非程序回滾是非常糟糕的。有三種情形的非程序回滾:
- 1. 系統(tǒng)回滾
- 2. 超時(shí)回滾
- 3. 資源回滾
系統(tǒng)回滾意味著程序服務(wù)器中的一些東西非常的糟糕,恢復(fù)的幾率很渺茫。超時(shí)回滾就是當(dāng)程序服務(wù)器中的程序處理請(qǐng)求時(shí)超時(shí);除非你把超時(shí)設(shè)置的很
短才會(huì)出現(xiàn)這種錯(cuò)誤。資源回滾就是當(dāng)一個(gè)程序服務(wù)器管理內(nèi)部的資源的時(shí)候發(fā)生錯(cuò)誤。例如,如果你設(shè)置你的程序服務(wù)器通過一個(gè)簡單的SQL語句去測(cè)試數(shù)據(jù)庫
的連接,但數(shù)據(jù)庫對(duì)于程序服務(wù)器來說是無法連接的,這個(gè)時(shí)候任何和這個(gè)資源相關(guān)的事情都會(huì)發(fā)生資源回滾。
如果發(fā)生非程序回滾,我們應(yīng)該立刻注意,這個(gè)是不小的問題,但是你也需要留意程序回滾發(fā)生的頻率。很多時(shí)候人們對(duì)發(fā)生的異常很敏感,因此你需要哪些異常對(duì)你程序來說才是重要的。
總結(jié)
盡管各個(gè)程序和他們的環(huán)境都各不相同,但是有一些共同的問題困擾著他們。這篇文章的注意力并不是放在程序代碼的問題上,因?yàn)榘炎⒁饬Ψ旁谝驗(yàn)榄h(huán)境的問題而導(dǎo)致的低性能的問題上:
- 1.內(nèi)存溢出
- 2.線程池大小
- 3.JDBC連接池大小
- 4.JDBC預(yù)先聲明語句緩存大小
- 5.緩存大小
- 6.池大小
- 7.執(zhí)行事務(wù)時(shí)候的回滾
為了有效的診斷性能的問題,你應(yīng)該了解什么問題會(huì)導(dǎo)致什么樣的癥狀。如果主要是程序的代碼導(dǎo)致的惡果那你應(yīng)該帶著問題去尋求負(fù)責(zé)代碼的人尋求幫助,但是如果問題是由環(huán)境引起的,那么就要依靠你的操作來解決了。
問題的根源依賴于很多要素,但是一些指示器可以增加一些你處理問題時(shí)候的一些信心,依靠他們可以完全排除一些其他的原因。我希望這個(gè)文章能對(duì)你排解JAVAEE環(huán)境問題起到幫助。
本文來自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u/24475/showart_1111150.html |
|