原來地址
http://blog.csdn.net/jaminwm/archive/2007/03/26/1541767.aspx
調(diào)優(yōu)背景
HBCZT信息中心使用
IBM X366
服務(wù)器
Windows2003
運行其基于
J2EE1.4
技術(shù)的應(yīng)用系統(tǒng)。另外運行一個基于
COM
技術(shù)的數(shù)據(jù)采集應(yīng)用程序。該程序客戶端讀入用戶填寫的
xls
格式表格文件信息,并通過該程序?qū)?/font>
XLS
內(nèi)容封裝成為
XML
并打包
ZIP
后發(fā)送到數(shù)據(jù)采集程序的服務(wù)器端,服務(wù)器端接受到文件后,對該
ZIP
包進行解包、并對解包后的
XML
信息進行解析、使用
SQL
逐條將記錄插入到
Oracle
數(shù)據(jù)庫中。數(shù)據(jù)庫連接池已經(jīng)設(shè)置為
20
,但批量數(shù)據(jù)插入數(shù)據(jù)庫的時候(數(shù)據(jù)量至少
500000
條記錄,一般情況
5000000
條記錄)導(dǎo)致數(shù)據(jù)庫異常緩慢。客戶希望找到系統(tǒng)瓶頸,并提出相應(yīng)性能調(diào)優(yōu)建議。
l
???????
總體思路
硬件調(diào)優(yōu)、操作系統(tǒng)調(diào)優(yōu),數(shù)據(jù)庫調(diào)優(yōu)
略!我們假設(shè)都已經(jīng)是最佳狀態(tài)。由于本人負責
WebLogic
部分的調(diào)優(yōu),所以以下思路與內(nèi)容均為
WebLogic
方面。特此說明
J2EE
應(yīng)用架構(gòu)環(huán)境下的系統(tǒng)調(diào)優(yōu),首先我們一般會從應(yīng)用程序出發(fā),去審核代碼,做到代碼級的優(yōu)化,然后再調(diào)整應(yīng)用服務(wù)器
(BEA WebLogic8.1)
和數(shù)據(jù)庫
(Oracle9i)
的參數(shù),最后當然是調(diào)整操作系統(tǒng)和網(wǎng)絡(luò)的性能
(
包括硬件升級
)
。這是一種
MDA
的先進做法。誠然,在這樣一個政務(wù)項目中,不可能完全按照這個思路來做,我們把目標首先定位在應(yīng)用系統(tǒng)所在的應(yīng)用服務(wù)器
(BEA WebLogic8.1)
上,通過對
BEA WebLogic8.1
的參數(shù)進行設(shè)置,使
WebLogic8.1
能夠在最優(yōu)化的環(huán)境中去運行其系統(tǒng),然后對
ORACLE
數(shù)據(jù)的參數(shù)進行優(yōu)化設(shè)置,最后進行性能測試再找出導(dǎo)致性能瓶頸所在的
SQL
代碼或
JAVA
程序,考量其修改的可行性,并進行最終問題優(yōu)先級認定,與瓶頸模塊進行協(xié)商解決性能問題。當然,一般情況下我見過的案例都是出現(xiàn)了性能問題后才想到調(diào)優(yōu),而且一般都是先進行系統(tǒng)參數(shù)調(diào)整,實在解決不了才會對代碼進行檢查
.
實際上,我們應(yīng)當將代碼級的調(diào)優(yōu)放在應(yīng)用設(shè)計時來做,測試生產(chǎn)時修改代碼將是一件極其痛苦的事情。
下表為一般性
J2EE
性能調(diào)優(yōu)的參照情況一覽表,供參考。
毛病
|
描述
|
癥狀
|
原因或治法
|
線性內(nèi)存泄漏
|
每單位
(
每事務(wù)、每用戶等
)
泄漏造成內(nèi)存隨著時間或負載線性增長。這會隨著時間或負載增長降低系統(tǒng)性能。只有重啟才有可能恢復(fù)。
|
隨著時間越來越慢
隨著負載越來越慢
|
雖然可能有多種外部原因,但最典型的是與資源泄漏有關(guān)
(
例如,每單位數(shù)據(jù)的鏈表存儲,或者沒有回收的回收
/
增長緩沖區(qū)
)
。
|
指數(shù)方式內(nèi)存泄漏
|
雙倍增長策略的泄漏造成系統(tǒng)內(nèi)存消耗表現(xiàn)為時間的指數(shù)曲線
|
隨著時間越來越慢
隨著負載越來越慢
|
通常是由于向集合
(Vector
,
HashMap)
中加入永遠不刪除的元素造成的。
|
糟糕的編碼:無限循環(huán)
|
線程在
while(true)
語句以及類似的語句里阻塞。
|
可以預(yù)見的鎖定
|
您需要對循環(huán)進行大刀闊斧的刪剪。
|
資源泄漏
|
JDBC
語句,
CICS
事務(wù)網(wǎng)關(guān)連接,以及類似的東西被泄漏了,造成對
Java
橋接層和后端系統(tǒng)的影響。
|
隨著時間越來越慢
可以預(yù)見的鎖定
突然混亂
|
通常情況下,這是由于遺漏了
finally
塊,或者更簡單點,就是忘記用
close()
關(guān)閉代表外部資源的對象所造成的。
|
外部瓶頸問題
|
后端或者其他外部系統(tǒng)(如鑒權(quán))越來越慢,同樣減緩了
J2EE
應(yīng)用服務(wù)器和應(yīng)用程序
|
持續(xù)緩慢
隨著負載越來越慢
|
咨詢專家(負責的第三方或者系統(tǒng)管理員),獲取解決外部瓶頸問題的方法。
|
外部系統(tǒng)
|
J2EE
應(yīng)用程序通過太大或太多的請求濫用后端系統(tǒng)。
|
持續(xù)緩慢
隨著負載越來越慢
|
清除冗余的工作請求
,成批處理相似的工作請求,把大的請求分解成若干個更小的請求,調(diào)整工作請求或后端系統(tǒng)
(
例如,公共查詢關(guān)鍵字的索引
)
等。
|
糟糕的編碼:
CPU
密集的組件
|
這是
J2EE
世界中常見的感冒。一些糟糕的代碼或大量代碼之間一次糟糕的交互,就掛起了
CPU
,把吞吐速度減慢到爬行的速度。
|
持續(xù)緩慢
隨著負載越來越慢
|
典型的解決方案就是數(shù)據(jù)高速緩存或者性能計數(shù)。
|
中間層問題
|
實現(xiàn)得很糟糕的橋接層
(JDBC
驅(qū)動程序,到傳統(tǒng)系統(tǒng)的
CORBA
連接
)
,由于對數(shù)據(jù)和請求不斷的排列、解除排列,從而把所有通過它的流量減慢到爬行速度。這個毛病在早期階段很容易與外部瓶頸混淆。
|
持續(xù)緩慢
隨著負載越來越慢
|
檢查橋接層和外部系統(tǒng)的版本兼容性。如果有可能,評估不同的橋接供應(yīng)商。如果重新規(guī)劃架構(gòu),有可能完全不需要橋接。
|
內(nèi)部資源瓶頸:過度使用或分配不足
|
內(nèi)部資源
(
線程、放入池的對象
)
變得稀缺。是在正確使用的情況下加大負載時出現(xiàn)過度使用還是因為泄漏?
|
隨著負載越來越慢
零星的掛起或異常錯誤
|
分配不足:根據(jù)預(yù)期的最大負載提高池的最大尺寸。過度使用:請參閱外部系統(tǒng)的過度使用。
|
不停止的重試
|
這包括對失敗請求連續(xù)的
(
或者在極端情況下無休止的
)
重試。
|
可以預(yù)見的鎖定
突然混亂
|
可能就是后端系統(tǒng)完全宕機。在這里,可用性監(jiān)控會有幫助,或者就是把嘗試與成功分開。
|
線程:阻塞點
|
線程在過于積極的同步點上備份,造成交通阻塞。
|
隨著負載越來越慢
零星的掛起或異常錯誤
可以預(yù)見的鎖定
突然混亂
|
可能同步是不必要的
(
只要重新設(shè)計
)
,或者比較外在的鎖定策略
(
例如,讀
/
寫鎖
)
也許會有幫助。
|
線程:死鎖
/
活動鎖
|
最普遍,這是您基本的“獲得順序”的問題。
|
突然混亂
|
處理選項包括:主鎖,確定的獲得順序,以及銀行家算法。
|
?
l
???????
調(diào)優(yōu)建議
通過分析其配置。我們發(fā)現(xiàn)
JDBC
連接池存在性能問題。
在
WebLogic
中就大量使用了池
:JDBC Connection Pool
、
Socket Pool
、
Object Pool
和
Thread Pool
。
I/O
操作中,
buffer
是必須的,特別是對大文件的操作,不然容易造成內(nèi)存溢出。字節(jié)操作最快,所以盡可能采用
write(byte[])
,
Buffered FileOutputStream
比
Buffered FileWriter
要快,因為
FileWriter
需要
Unicode
到
Byte
的轉(zhuǎn)換。
JDBC
建議使用
buffer
和
cache
。為
HttpServletResponse
設(shè)置
buffersize
,使用
wl-cache
,緩存在
JNDI
樹上獲取的對象等等。
此外,使用
JDK 1.4
的非阻塞
I/O
對性能也有很大提高。
JDBC
代碼調(diào)優(yōu)最大的原則就是使用
WebLogic
的連接池,而不是自己直連數(shù)據(jù)庫。在我接觸的很多自己實現(xiàn)連接池的項目中,大部分遇到死鎖和連接泄漏的問題,最后得不得修改代碼。而
WebLogic
提供了功能強大,性能良好的數(shù)據(jù)庫連接池,我們要做的只是封裝一個連接管理類,從
JNDI
樹上獲取數(shù)據(jù)源并緩存,得到連接,并提供一系列關(guān)閉數(shù)據(jù)庫資源的方法。
對任何資源使用的原則是用完即關(guān),不管是數(shù)據(jù)庫資源、上下文環(huán)境,還是文件。數(shù)據(jù)庫資源的泄漏極易造成內(nèi)存泄漏,乃至系統(tǒng)崩潰。在使用完數(shù)據(jù)庫資源后依次關(guān)閉
ResultSet
,
Statement
和
Connection
,而在一個數(shù)據(jù)庫連接多次進行數(shù)據(jù)庫操作時要特別注意
ResultSet
和
Statement
依次關(guān)閉。
由于獲取連接時默認自動提交方式,使用
connection.setAutoCommit(false)
關(guān)閉自動提交,使用
PreparedStatement
,批量更新,業(yè)務(wù)復(fù)雜或者大數(shù)據(jù)量操作時使用存儲過程,盡量使用
RowSet
,此外設(shè)置記錄集讀取緩存
FetchSize
和設(shè)置記錄集讀取方向
FetchDirection
對性能也有一定的提高。
Servlet
代碼調(diào)優(yōu)比較簡單:在
Servlet
之間跳轉(zhuǎn)時,
forward
比
sendRedirect
更有效;設(shè)置
HttpServletResponse
緩沖區(qū),如:
response.setBufferSize(20000);
在
init()
方法里緩存靜態(tài)數(shù)據(jù),而在
destroy()
中釋放它;建議在
Servlet
里使用
ServletOutputStream
輸出圖片等對象;避免在
Servlet
和
Jsp
中定界事務(wù)等。
JDBC Connection Pool
的調(diào)優(yōu)受制于
WebLogic Server
線程數(shù)的設(shè)置和數(shù)據(jù)庫進程數(shù),游標的大小。通常我們在一個線程中使用一個連接,所以連接數(shù)并不是越多越好,為避免兩邊的資源消耗,建議設(shè)置連接池的最大值等于或者略小于線程數(shù)。同時為了減少新建連接的開銷,將最小值和最大值設(shè)為一致。增加
Statement Cache Size
對于大量使用
PreparedStatement
對象的應(yīng)用程序很有幫助,
WebLogic
能夠為每一個連接緩存這些對象,此值默認為
10
。在保證數(shù)據(jù)庫游標大小足夠的前提下,可以根據(jù)需要提高
Statement Cache Size
。比如當你設(shè)置連接數(shù)為
25
,
Cache Size
為
10
時,數(shù)據(jù)庫可能需要打開
25*10=250
個游標。不幸的是,當遇到與
PreparedStatement Cache
有關(guān)的應(yīng)用程序錯誤時,你需要將
Cache Size
設(shè)置為
0
。盡管
JDBC Connection Pool
提供了很多高級參數(shù),在開發(fā)模式下比較有用,但大部分在生產(chǎn)環(huán)境下不需調(diào)整。這里建議最好不要設(shè)置測試表,
同時
Test Reserved Connections
和
Test Released Connections
也無需勾上。
當然如果你的數(shù)據(jù)庫不穩(wěn)定,時斷時續(xù),你就可能需要上述的參數(shù)打開。
最后分析一下
JDBC
驅(qū)動程序類型的選擇,
Oracle
提供
thin
驅(qū)動和
oci
驅(qū)動,從性能上來講,
oci
驅(qū)動強于
thin
驅(qū)動,特別是大數(shù)據(jù)量的操作。但在簡單的數(shù)據(jù)庫操作中,性能相差不大。所以我建議對數(shù)據(jù)量至少
500000
條記錄,一般情況
5000000
條記錄的狀況使用
oci
驅(qū)動。
通過分析其日志并使用
GC
資源查看。我們發(fā)現(xiàn)存在內(nèi)存泄露的垃圾回收失敗問題。
垃圾收集
(GC)
是指
JVM
釋放
Java
堆中不再使用的對象所占用的內(nèi)存的過程,而
Java
堆
(Heap)
是指
Java
應(yīng)用程序?qū)ο笊娴目臻g。堆大小決定了
GC
的頻度和時間。堆越大,
GC
頻度低,速度慢。堆越小,
GC
頻度高,速度快。所以
GC
和堆大小是一組矛盾。為了獲取理想的
Heap
堆大小,需要使用
-verbosegc
參數(shù)
(Sun jdk: -Xloggc:)
以打開詳細的
GC
輸出。分析
GC
的頻度和時間,結(jié)合應(yīng)用最大負載所需內(nèi)存情況,得出堆的大小。通常情況下,我們建議使用可用內(nèi)存
(
除操作系統(tǒng)和其他應(yīng)用程序占用之外的內(nèi)存
)70-80%
,為避免堆大小調(diào)整引起的開銷,設(shè)置內(nèi)存堆的最小值等于最大值即
:-Xms=-Xmx
。而為了防止內(nèi)存溢出,建議在生產(chǎn)環(huán)境堆大小至少為
256M(Platform
至少
512M)
,實際環(huán)境中
512M~1G
左右性能最佳,
2G
以上是不可取的,在調(diào)整內(nèi)存時可能需要調(diào)整核心參數(shù)進程的允許最大內(nèi)存數(shù)。對于
sun
和
hp
的
jvm
,永久域太小
(
默認
4M)
也可能造成內(nèi)存溢出,應(yīng)增加參
-XX:MaxPermSize=128m
。建議設(shè)置臨時域
-Xmn
的大小為
-Xmx
的
1/4~1/3
,
SurvivorRatio
為
8
。
為了獲得更好的性能,建議在啟動文件設(shè)置
WebLogic
為產(chǎn)品模式,此時
sun
和
hp jvm JIT
引擎為
-server
,默認情況下打開
JIT
編譯模式對性能也有幫助。調(diào)整
Chunk Size
和
Chunk Pool Size
也可能對系統(tǒng)的吞吐量有提高。此外還需關(guān)閉顯示
GC: -XX:+DisableExplicitGC
。
建議
Intel
平臺上使用
jRockit
(使用參數(shù)
-jrockit
)無疑大大提高
WebLogic
性能。本系統(tǒng)使用
SUN JDK1.4.2_08
。
jRockit
支持四種垃圾收集器:分代復(fù)制收集器、單空間并發(fā)收集器、分代并發(fā)收集器和并行收集器。默認狀態(tài)下,
JRockit
使用分代并發(fā)收集器。要改變收集器,可使用
-Xgc:
,對應(yīng)四個收集器分其他為
gencopy
,
singlecom
,
gencon
以及
parallel
。為得到更好的響應(yīng)性能,應(yīng)該使用并發(fā)垃圾回收器:
-Xgc:gencon
,可使用
-Xms
和
-Xmx
設(shè)置堆棧的初始大小和最大值,要設(shè)置護理域
-Xns
為
-Xmx
的
10%
。而如果要得到更好的性能,應(yīng)該選用并行垃圾回收器
:-Xgc: parallel
,由于并行垃圾回收器不使用
nursery
,不必設(shè)置
-Xns
。
jRockit
還提供了強大的圖形化監(jiān)控工具
Jrockit Management Console
。可以查看
GC
性能問題。
通過實時查看并分析操作系統(tǒng)與
ORACLE
系統(tǒng)中的
I/O
信息。我們發(fā)現(xiàn)存在
I/O
讀寫占用資源率高且頻繁問題。
WebLogic Server有兩套套接字復(fù)用器:Java版和本地庫。采用小型本地庫更有效,盡量激活Enable Native IO(默認),此時UNIX默認使用CPUs+1個線程,Window下為雙倍CPU。如果系統(tǒng)不能加載本地庫,將會拋出一個異常:java.lang.UnsatisfiedLinkException,此時只能使用Java套接字復(fù)用器,可以調(diào)整socket readers 百分比,默認為33%。該參數(shù)可以在Console Server Tuning Configuration配置欄里設(shè)置。