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