Hibernate深入淺出(十三)Hibernate性能優化

在實際開發中,如何對基於Hibernate的持久層實現進行進一步的性能調整,其中涉及到怎樣的技術和技巧,下面需要進行探討。
性能調整的第一步,可能也是最關鍵的一步:性能檢測。
1. 性能檢測
從技術角度而言,除非設計上的嚴重缺陷,否砸,絕大部分時候,只要能發現性能瓶頸所在,總是能通過各種途徑對系統進行調整,從而獲得進一步的性能提升。
那麼如何發現系統運行中的性能缺點,並尋出其中最關鍵的部分?在系統的不同層面往往有着不同的檢測方式與技巧。
常用的有以下幾種性能觀測手段:
1)源代碼評審
2)追加性能監測代碼
3)基於工具的性能分析與檢查


1)源代碼評審
其中,源代碼評審機制的監測力量無疑最爲薄弱。特別在中大型項目中,代碼動輒以10萬行計,面對如此龐雜的代碼羣,基於人力的代碼評審幾乎形同虛設。
2)追加性能監測代碼
通過追加性能監測代碼進行性能分析可能是日常開發中最常用的一種手段。在之前“數據批量操作”部分內容中,通過在代碼中追加計時功能,我們得到了代碼片段的執行時間,並以此作爲性能比較的依據。
最基礎,也是最直接的觀察渠道。不過缺陷也顯而易見。
首先,它並不能稱爲一個工程化的性能監測手段,過多的監測代碼混雜到在邏輯代碼之間,使得代碼可維護性降低。其次,不同開發人員對性能監測的認知程度並不一致,導致性能觀察的結果較爲混亂和片面,難以全面的反應出系統整體性能指標。
其次,性能觀測結果難以進行分析和統計,我們得到的結果往往是某個代碼片斷所消耗的時間,而無法對系統性能進行全面的衡量(如,系統中哪些方法調用頻率最高,哪些消耗時間最長,及其之間對等關係如何)。
最爲關鍵的一點,我們甚至無法知道系統整體的併發容量到底如何。
3)基於工具的性能分析與檢查
“工具”是技術羣體工作經驗積累的結晶。
在系統性能監測領域,目前市場上已經有了衆多解決方案。其中包括商業的性能監測、評估系統(如Load Runner),以及免費甚至開源的性能監測軟件,合理利用這些工具將對形成理性的、可衡量的性能分析策略有着莫大的幫助。
下面就圍繞持久層中常用的通用性能監測工具P6SPY進行介紹。

2. P6SPY
P6SPY是針對數據庫訪問操作的動態監測框架(開源項目,項目首頁:www.p6spy.com)。經過長時間的發展,P6SPY已經日漸成熟,具備豐富的文檔和周邊工具資源。
從實現原理上來講,P6SPY模擬了一個標準的JDBC Driver,它代理了真正的底層JDBC驅動程序。
也就是說,我們只需將系統所用的JDBC驅動切換到P6SPY JDBC Driver,同時將P6SPY的JDBC配置到我們實際的JDBC驅動,即可使用其提供的數據訪問性能監測功能。
以Hibernate配置(hibernate.cfg.xml)爲例。首先,從P6SPY站點下載最新軟件包p6spy-install.zip。從壓縮文件中解壓出:
a)    p6spy.jar
b)    spy.properties
其中p6spy.jar爲運行包,spy.properties爲對應的配置文件。
將p6spy.jar加入項目文件的CLASSPATH,同時將spy.properties放入運行環境的根目錄(Eclipse中,將其置於src目錄根節點之下)。
修改配置文件hibernate.cfg.xml,將hibernate.connection.driver_class修改爲P6SPY提供的JDBC Driver Class:

<session-factory>
        <property name="hibernate.connection.url">
            jdbc:jtds:sqlserver://localhost/SampleDB
        </property。
        <property name="hibernate.connection.driver_class">
            com.p6spy.engine.spy.P6SpyDriver
            <!---net.sourceforge.jtds.jdbc.Driver-->
        </property>
        …
</session-factory>

完成了Hibernate中的JDBC切換,我們還需要對P6SPY進行配置(spy.properties),指定底層用於實際操作的JDBC驅動:

…
# the JTDS open source driver
realdriver = net.sourceforge.jtds.jdbc.Driver
…

這樣,P6SPY的基本配置即告完成。可以看到,只需簡單地切換JDBC驅動組件,我們就成功地將P6SPY與系統無縫銜接。
P6SPY通過JDBC代理的形式,在應用程序與底層JDBC之間嵌入了一個透明中間層。那麼通過這樣的機制,P6SPY能爲我們帶來些什麼?
我們知道,Hibernate在執行底層數據庫操作時,依然是基於JDBC訪問接口,也就是說,Hibernate所有的持久化操作,最終必然通過JDBC提交。
那麼,位於應用與JDBC層之間的這個P6SPY代理就顯得別具意義,由於可以截獲所有的JDBC操作,P6SPY可以輕易實現SQL執行效率的判定以及執行結果的分析統計。如某條SQL的執行時間,執行次數,及其在全部SQL中的相對執行頻度,相對時間消耗比率。這樣,我們就可以輕鬆找出對系統性能影響最大的SQL操作,並對其進行優化。
另外,對於使用佔位符(如PreparedStatement中,我們以?作爲佔位符,之後填充參數值)的Statement而言,P6SPY可以輸出SQL及其實際的參數值。這也大大提高了程序調試的直觀性。
默認情況下,P6SPY的日誌文件爲spy.log,當然我們也可以通過調整spy.properties配置文件中的參數進行指定。
spy.log輸出示例如下:

其中”1109352762092|20|0|statement…”就是我們執行的Statement,以|分隔的第二欄,即此SQL執行所消耗的時間。
可以看到,這樣瑣碎的日誌文件,解讀工作非常繁瑣,且難以統計,雖然我們可以看出其中每條Statement執行的時間,但是我們難以對全局進行把握。我們還需要某條特定Statement的執行頻度以及資源消耗比等更加全局性的統計結果。
前面說過,p6spy經過長期發展,已經涌現了很多周邊資源,其中,SQL  Profiler爲我們提供了一個圖形化的監控界面,它可以實時監控SQL執行過程,對執行結果進行統計並加以優化。
SQL Profiler不但爲我們提供了直觀的Statement統計信息,它還會根據當前SQL的執行效能給出進一步優化的建議(如建議在某些字段上建立索引)。
首先在www.jahia.org下載最新的SQL Profiler軟件包。軟件包中包含了兩個主要文件:
a)    Sqlprofiler.jar
b)    spy.properties
spy.properties是一個P6SPY的示例配置文件。
sqlprofiler.jar則是可執行的jar文件包(可通過命令行”java –jar sqlprofiler.jar”運行),其中包含了一個SWING的用戶控制界面。SQL  Profiler啓動後,即開始監聽本機4445端口。SQL Profiler正是通過這個端口監聽來自P6SPY的Statement執行日誌(這樣即可在不同機器上分別運行監控與應用程序,同時也避免了互相之間的性能干擾)。
P6SPY通過log4j的SocketAppender向SQL Profiler發送日誌信息,我們查看SQL Profiler軟件包中的示例配置文件spy.properties,就可以發現如下SocketAppender配置片斷:

log4j.appender.SQLPROFILER_CLIENT=org.apache.log4j.net.SocketAppender
log4j.appender.SQLPROFILER_CLIENT.RemoteHost=localhost
log4j.appender.SQLPROFILER_CLIENT.Port=4445
log4j.appender.SQLPROFILER_CLIENT.LocationInfo=true

爲了簡單起見,我們用SQL Profiler中的spy.properties文件覆蓋之前的版本,更改realdriver配置以符合我們的實際情況。同時啓動SQL Profiler.jar(如果報告OutOfMemoy錯誤,則以命令行”java –Xmx256m jar sqlprofiler.jar”啓動)(如下圖)。

可以看到,界面上半部的SQL Satements欄目,顯示了當前正在執行的Statement的實時信息況,每秒刷新。
中部的Profile results欄,用於顯示優化器的優化結果。當我們按下工具欄上的暫停按鈕時,SQL Profiler即開始進行優化分析工作,並將結果顯示在這個欄目中,從示例圖中可以看到,SQL Profiler建議我們在t_user表的name字段上建立索引。這些優化建議我們可以通過工具欄上左側的第一個按鈕保存,對於這裏的實例,優化文件內容爲:
CREATE INDEX t_user_index on t_user(name);
界面下部的detail欄目則顯示了SQL Statements欄目中當前選定Statement的詳細信息。
切換到logger頁,我們可以看到P6SPY的運行日誌(下圖)。

切換到Analysis頁,可以看到類似下圖所示的結果:

從以上圖中,我們可以看到之前數據庫操作過程中的統計信息,包括查詢的次數統計,查詢所導致的數據流量統計(越大的流量意味着越高的CPU和IO資源消耗)等。且在運行過程中,這些統計信息也會實時刷新。
雖然簡單,但即使是這樣簡單的工具,也可以爲我們的性能監測提供極爲重要的參考依據。實際上,性能優化的關鍵往往也就主要集中在這幾個有限的參數上,靈活地利用這些簡單的工具,往往也可以達到意想不到的效果。
對於持久層的性能監測,暫且介紹這些內容。前面說過,無論在商業領域還是開源社區,類似的工具非常豐富,各具特色,如何利用這些工具達到我們所期望的目的,所有的限制可能僅僅是我們的現象力而已。
與SQL Profiler類似的工具還有IronTrack SQL以及商業性能監測工具——JDBInsight等,它們提供了更加豐富的特性和更爲強大精準的監測功能。

3. Hibernate常見優化策略
針對不同類型的應用,具體的性能優化策略可能千差萬別。這裏,我們對之前的內容進行了總結,提供了一些通用的優化思路,希望有所啓發。
〉在允許的情況下,選用最新版本的Hibernate發行版
Hibernate3提供了一些有助於性能提高的新特性,如經過優化的批量處理機制、代理機制、屬性的延遲加載支持等。
〉制定合理的緩存策略
在之前的內容中,反覆強調了緩存對於整體性能的關鍵性影響。制定合理的緩存策略將是提高性能的一個有效途徑,在系統設計後期,建議根據應用中每個庫表的實際情況,爲其指定相匹配的緩存模式,同時通過系統壓力測試以得到最佳的緩存性能。
〉採用合理的Session管理機制
在Hibernate實用技術中,我們介紹了基於ThreadLocal的Session管理機制,通過Thread級別的Session重用,我們可以通過充分利用一級緩存中的已有數據,避免無謂的數據庫訪問開銷和臨時對象的反覆創建,請在您的系統中考慮追加這項特性,或根據情況制定符合實際情況的Session管理策略。
〉儘量使用延遲加載特性
對於無需立即加載的數據,應通過延遲加載特性應需加載,以避免系統資源的無謂消耗。
〉設定合理的批處理參數(batch_size)
〉如果可能,選用UUID作爲主鍵生成器
〉如果可能,選用基於version的樂觀鎖策略替代悲觀鎖
〉開發過程中,打開Hibernate的SQL日誌輸出(hibernate.show_sql),通過觀察Hibernate生成的SQL語句進一步瞭解其實現原理,從而指定更好的實現策略。
以上這些優化建議大多來自日常開發工作中的經驗積累。值得注意的是,對於基於Hibernate實現的持久層,其性能表現取決於多個方面的綜合因素。其中,數據庫本身的優化也起着舉足輕重的作用,合理的索引、緩存與數據分區策略都會對持久層性能帶來可觀提升。關於數據庫本身的優化這裏就不再加以展開。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章