posts - 40,  comments - 7,  trackbacks - 0
          速度瓶頸問題的提出

            在企業級的Java應用中,訪問數據庫是一個必備的環節。數據庫作為數據資源的集散地,往往位于企業級軟件體系的后方,供前方的應用程序訪問。在Java技術的體系中,應用程序是通過JDBC(Java Database Connectivity)接口來訪問數據庫的,JDBC支持"建立連接、SQL語句查詢、處理結果"等基本功能。在應用JDBC接口訪問數據庫的過程中,只要根據規范來操作,這些功能的實現不會出差錯。但是,有些時候進行數據查詢的效率著實讓開發人員懊惱不已,明明根據規范編寫的程序,卻得不到預期的運行效果,造成了整個軟件的執行效率不高。

            起初,我們把問題歸結于Java字節碼加載和執行速度的緩慢,緊接著硬件的功能普遍得到了增強,證明這樣的想法些許是錯誤的,還沒有抓到真正的根本原因。本文將逐步解剖JDBC訪問數據庫的機制,深層分析造成這種速度瓶頸問題的原因,并提出在現有的Java技術框架下解決這個速度瓶頸問題的思路和方法。

            JDBC訪問數據庫的機制

          ????????????????
          圖1????????????????????????????????????????????????圖2

            圖1和圖2描述了Java應用程序通過JDBC接口訪問數據庫的4種驅動模式,也就是底層實現JDBC接口的模式。對于這些模式,我們逐一介紹:

            模式4:圖1左邊的分支稱為模式4,它一般是數據庫廠商才能實現的純Java的基于本地協議的驅動,直接調用DBMS(數據庫管理系統)使用的網絡協議,對于企業內部互聯網來說,是一個實用的解決方案。

            模式3:圖1右邊的分支稱為模式3,它同樣是一個純Java驅動,不同于模式4的是基于網絡協議。它的機制是將JDBC調用轉換為中間網絡協議,然后轉換為DBMS協議。中間網絡協議層起到一個讀取數據庫的中間件的作用,能夠連接許多類型的數據庫,因而是最靈活的JDBC模式。這種模式的產品比較適用于企業內部互聯網,如若支持國際互聯網,還需添加對安全、穿過防火墻訪問等的支持。

            模式1:圖2左邊的分支稱為模式1,即通常由Sun公司提供的JDBC-ODBC橋接器。它提供了經由一種或多種ODBC驅動進行訪問的JDBC接口,而ODBC驅動,在很多情況下也即數據庫的客戶端,必須加載到客戶機。因而,它適用于下載和自動安裝Java程序不重要、實驗用途或者沒有其它JDBC驅動可用的情況下。

            模式2:圖2右邊的分支成為模式2,類似于JDBC-ODBC橋接器,需要加載到客戶機,卻是一個部分用Java實現的驅動接口。它將JDBC調用轉換為對數據庫(Oracle、Sybase、Informix、DB2等)客戶端接口的調用。

            不同模式的JDBC接口的選擇

            以上闡述的JDBC接口的模式不同,讓我們可以把JDBC接口按照實現的模式分為四類。有些同仁可能有這樣的體會,選擇不同的JDBC接口會有不同的訪問速度,為何會出現這樣的情況?這個問題的答案是,不同的應用需要不同模式的JDBC接口,因而我們在面對一個應用時,要慎重選擇JDBC接口。

            通常的DBMS都支持微軟提出的ODBC規范,因而模式1可當作您在設計和實現軟件時的選擇,它易于配置的特性能夠讓你把選擇JDBC等煩惱的問題暫且拋在一邊,讓自己的Java程序能夠及早地正常工作起來。

            一般說來,商業DBMS的提供者往往會為自己的數據庫提供一個JDBC接口,應用的是模式4。這種模式的優勢在于和數據庫本身結合比較緊密,而且是純Java的實現,在企業級的軟件應用中,應該是首選。例如,對于Oracle數據庫來說,有Oracle、SilverStream、DataDirect等公司提供這種類型的驅動,其性能往往被評價為最高效的、最可靠的驅動程序。但偶爾也有比較麻煩的情況,例如微軟就不會提供MS SQL的JDBC接口,這時就需要到Sun的網站(http://industry.java.sun.com/products/jdbc/drivers)查找相關的模式4驅動,上面提到的DataDirect公司(http://www.datadirect-technologies.com/jdbc/jdbc.asp)就提供了支持MS SQL的模式4驅動,只是你需要支付750$購買這個JDBC驅動。

            同樣是純Java實現的模式3,與模式4相比,優勢在于對多種數據庫的支持,體現了其靈活性。在大型的企業級的軟件應用中,后臺數據庫往往不是一個,而且是由不同的廠商支持的。不過,模式3的JDBC驅動往往提供許多企業級的特征,例如SSL安全、支持分布式事務處理和集中管理等,因而會對你特殊的用途有很大的幫助。是否選用,還在于你對擴展應用是否有需求以及對多DBMS的支持。

            談到這兒,我對模式3和模式4作一個總結:兩者都是純Java實現的驅動,因而不需要數據庫廠商提供附加的軟件,就可以運行在任何標準的Java平臺,性能上比較高效、可靠。

            了解上述3種JDBC的實現模式之后,模式2就更容易闡釋了,你可以理解它為前三者利弊平衡的妥協產物:

            1. 借鑒模式1利用客戶機本地代碼庫,加速數據訪問的執行,但卻摒除ODBC標準,而是支持廠商自己指定的性能擴展
            2. 借鑒模式3利用多層結構,上層用Java實現,利于跨平臺應用和支持多數據庫,但下層卻改為本地代碼,加速執行速度
            3. 借鑒模式4和數據庫結合緊密的優點,部分用Java實現,更是對數據庫性能有很大的擴展

            這種開放和高性能的特征得到了業界的肯定,因而被主要的數據庫廠商強烈推薦。盡管它需要你下載本地代碼庫到客戶機,但相對于你訪問數據庫速度的提高,這些應該只是舉手之勞了。下面對4種實現JDBC的模式選擇,歸納一下選擇的順序(當然是指你有選擇余地的時候,不存在的話向后推延):

          編號 選擇過程分析 選擇順序
          1 實驗性環境下,盡可能選擇易于配置的驅動,利于Java程序的開發,后期可在對應用環境進行判斷后,再對JDBC模式進行選擇 1>2>3>4
          2 小型企業級環境下,不需要對多數據庫的支持,因而模式2和3的有些優點并不能體現出來,強烈推薦你選擇模式4的JDBC驅動 4>2=3>1
          3 大型企業級環境下,需要對多數據庫的支持,模式2和3各有千秋,但是更多情況下是你會選擇速度較快的模式2 2>3>4>1

            對于不同廠商提供的但應用相同模式的JDBC接口,理論上比較不出效率的高低,你只有通過一定的工具,例如Benchmark等,對它們進行比較才能更有利于你的選擇。因為暫時不存在第三方提供的數據比較結果,所以這些問題需要你對上述內容有了透徹理解之后自行解決。

            Java程序中SQL語句格式的優化

            這個時候,你也許還在為找不到合適的JDBC驅動而一籌莫展,也許為自己在凌晨3點下載的JDBC驅動通過了測試而欣喜若狂,但是并不說明你對程序的優化工作已經無關緊要了。切記,對整個軟件系統的優化,包括每個環節的優化,要不有可能你會前功盡棄。我在這兒不和大家討論Java程序的算法,而是簡單闡述一下選擇SQL語句格式的必要和如何選擇對自己有利的SQL語句格式。看下面兩段程序片斷:

            Code Fragment 1:

            String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERE COF_NAME LIKE 'Colombian'";
            stmt.executeUpdate(updateString);

            Code Fragment 2:

            PreparedStatement updateSales = con.prepareStatement("UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? ");
            updateSales.setInt(1, 75);
            updateSales.setString(2, "Colombian");
            updateSales.executeUpdate();

            片斷2和片斷1的區別在于,后者使用了PreparedStatement對象,而前者是普通的Statement對象。PreparedStatement對象不僅包含了SQL語句,而且大多數情況下這個語句已經被預編譯過,因而當其執行時,只需DBMS運行SQL語句,而不必先編譯。當你需要執行Statement對象多次的時候,用PreparedStatement對象將會大大降低運行時間,當然也加快了訪問數據庫的速度。

            這種轉換也給你帶來很大的便利,不必重復SQL語句的句法,而只需更改其中變量的值,便可重新執行SQL語句。選擇PreparedStatement對象與否,在于相同句法的SQL語句是否執行了多次,而且兩次之間的差別僅僅是變量的不同。如果僅僅執行了一次的話,它應該和普通的Statement對象毫無差異,體現不出它預編譯的優越性。

            軟件模型中對數據庫訪問的設計模式的優化

            在我閱讀J2EE藍圖和JDO草案的過程中,我發現了訪問模式對數據庫訪問的影響,因而想在本文中闡述如何針對自己的軟件需求選擇合適的軟件模式。

            J2EE藍圖的設計者在Java Pet Store示例應用中使用了MVC(Model-View-Controller)體系,給許多J2EE設計模式提供了背景。我要談及的三種設計模式是:Data Access Object、Fast Lane Reader、Page-by-Page Iterator,它們為加快數據存取速度提供了一些可以在系統設計階段值得我們借鑒的想法。

            Data Access Object

            將商業邏輯從數據存取邏輯中分離出來,把存取的資源改編,從而使資源可以容易和獨立地轉變。

            依賴于底層數據資源的特殊要素(例如數據庫的供應商)的商業組件,常將商業邏輯和數據存取邏輯配合起來,只能使用特殊類型的資源,而使用不同類型的資源時,復用將會非常困難,因此,只能服務于有限的市場領域。DAO(Data Access Object)即是將數據存取邏輯從EJB中抽去出來抽象為一個獨立的接口,EJB根據接口的操作執行商業邏輯,而接口針對使用的數據資源實現為DAO對象。

            在Java Pet Shop這個例子中,OrderEJB組件通過關聯的OrderDAO類訪問數據庫,自身則關注于商業邏輯的實現。在調度階段,將配置某一類(OrderDAOCS、OrderDAOOracle或OrderDAOSybase)為OrderDAO的實現,而OrderEJB無須任何更改。圖3更能幫助你明白其中的道理:


          圖3 Data Access Object的設計模式

            此舉增加了數據存取的彈性、資源的獨立性和擴展性,但復雜度有相應的提高,其它附帶的問題我們不在這兒討論。

            Fast Lane Reader

            拋棄EJB,加速只讀數據的存取。

            有些時候,高效地存取數據比獲得最新的數據更重要。在Java Pet Store中,當一個用戶瀏覽商店的目錄時,屏幕與數據庫內容吻合不是至關緊要的,相反,迅速顯示和重新獲得非常重要。FLR模式可以加速從資源中重新獲得大型的列數據項的速度,它不用EJB,而是更直接地通過DAO來存取數據,從而消除EJB的經常開支(例如遠程方法調用、事務管理和數據序列化等)。

            在Java Pet Store這個例子中,當一個用戶瀏覽目錄時,通過CatalogDAO(而不是CatalogEJB)從數據庫加載數據項,而CatalogDAO是一個Fast Lane Reader的實例,使得讀訪問變得迅速,如圖4:


          圖4 Fast Lane Reader設計模式

            與DAO模式不同的是,FLR是一個優化的模式,但不是要替代原有的訪問機制,而是作為補充使其完備。當你頻繁地只讀大型的列數據和不必存取最新的數據時,使用FLR將是非常合適的。

            Page-by-Page Iterator

            為了高效地存取大型的遠程數據列,一下子通過重新獲得其元素為一個子列的Value Object(提高遠程傳輸效率的設計模式,這兒不詳盡描述)。

            分布式數據庫的應用經常需要用戶考慮一長列數據項,例如一個目錄或一個搜索結果的集合。在這些情況下,立刻提供全列的數據經常不必要(用戶并不是對所有的數據項感興趣)或不可能(沒有足夠的空間)。此外,當重新獲得一列數據項時,使用Entity Bean的代價將非常高昂,一個開銷來自于使用遠程探測器來收集Requested Bean,另外,更大的開銷來自對每個Bean產生遠程調用以從用戶獲得數據。

            通過Iterator,客戶機對象能一下子重新獲得一個子列或者頁的Value Object,每一頁都剛好滿足客戶機的需求,因此,程序使用較少的資源滿足了客戶機的立刻需求。

            在Java Pet Store這個例子中,JSP頁面product.jsp任何時候只顯示一個數據列的一部分,從ProductItemListTag(Page-by-Page Iterator)重新獲得數據項,當客戶機希望看到列中的別的數據項,product.jsp再次調用Iterator重新獲得這些數據項,流程見圖5:


          圖5 Page-by-Page Iterator設計模式

            以上設計模式的應用向我們表明,在某些特殊情況下,優化對數據庫的訪問模型,既可滿足用戶的需求又可提高訪問數據庫的效率。這給我們一個思路,就是:在你的硬件環境可能會產生瓶頸的情況下,可以通過對軟件模型的優化來達到滿足需求的目的。上述三種設計模式的應用情形為:

          Data Access Object 需要將商業邏輯和數據存取邏輯分離;
          在調度的時刻,需要支持選擇數據源的類型;
          使用的數據源的類型的變化,對商業對象或其它客戶端完成數據存取沒有影響。
          Fast Lane Reader 面對大型的列數據,需要經常的只讀訪問;
          訪問最新的數據并不是至關緊要的事情。
          Page-by-Page Iterator 存取大型的服務器端數據列;
          任何時刻,用戶只對列的一部分內容感興趣;
          整個列的數據不適合在客戶端顯示;
          整個列的數據不適合在存儲器中保存;
          傳輸整個列的數據將耗費太多的時間。

            在顯示商品目錄的時候,我們選擇了DAO和FLR的結合,因為它們兩者的條件都得到了滿足(需要分離商業邏輯和數據存取邏輯,經常的只讀訪問和對即時性不敏感),此時應用將會大大發揮它們的優點。而在進行內容檢索的時候,我們會選擇PPI,因為也許檢索出了上千條的記錄,但是用戶沒有興趣立即閱讀全部內容,而是一次十條地閱讀,或者他在閱讀完前十條記錄后發覺自己的目的已經達到,接下來瀏覽別的網頁了,都不必我們一次性地傳輸上千條記錄給他,所以也是PPI的應用條件得到了滿足,結果則是此模式的優點得到了發揮,又不影響全局的數據訪問。

            在進行軟件模型的設計時,整體的框架可以應用某些優秀的、通用的設計模式,這樣既加快模型的建立速度,又能和其它系統集成。但是,碰到一些瓶頸問題的情況下,我們就需要對局部的設計模式做一些調整,以優化整個系統,上述三個模式就是對原有體系的補充,它們并沒有對整體的框架做出巨大的改變,卻突破了某些瓶頸(瓶頸往往是局部的)障礙,讓我們的產品更好地服務于用戶。

            將深入研究的問題

            開篇至今,我們主要探討了軟件層次上的解決問題,但是,必須肯定一點,如果你的硬件環境非常差(運行Java都有困難)或非常好(額外的存儲空間、超快的運算速度和富裕的網絡帶寬),上述途徑對你來說很難有大的幫助。前一種情況,我建議你升級硬件設備到軟件廠商推薦的配置(強烈反對最小配置),以使應用服務器、數據庫、Java等軟件能夠運行自如;后一種情況,我沒什么話可說,花錢是解決這個問題最好的辦法。

            本文并未談及線程池和告訴緩沖這兩個非常重要的概念,因為筆者認為,它們是針對局部時間高訪問量的瓶頸問題的解決,不能理解為簡單的速度瓶頸問題,所以我會在下一篇文章中分析這種特殊的情況和提出解決問題的辦法。也許你對這一點更關心一些,認為自己的問題就出在這個地方,這是非常好的思考問題的方式,你已經抓住了問題的關鍵。但是,我還是建議你通讀一下本文,讓自己對速度瓶頸問題有更好的理解,并掌握在解決問題的過程中,分辨常態和暫態,從而選擇不同的思路入手。其實,本文談及的就是速度瓶頸問題的常態,而下一篇文章討論的將會是暫態,希望你能夠漸入佳境。

            JDO(Java Data Object)是需要我們關注的一個API,它定義了新的數據存取模型,直接借鑒了DAO設計模式。不同的數據源,有不同的數據存取技術,就有不同的API供開發人員使用。JDO正是為了解決這個問題而產生的,它實現了即插即用的數據存取的實現和持久信息(包括企業數據和本地存儲的數據)以Java為中心的視圖。因此,開發人員只關注創建那些實現商業邏輯的類和用它們來表現數據源的數據,而這些類和數據源之間的映射則由那些EIS領域的專家來完成。如果大家對JDO感興趣的話,那么我會寫第三篇文章把其詳細介紹給大家,并給出示例應用。

          posted on 2006-08-22 09:32 Lansing 閱讀(441) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2006年8月>
          303112345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          歡迎探討,努力學習Java哈

          常用鏈接

          留言簿(3)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          Lansing's Download

          Lansing's Link

          我的博客

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 浪卡子县| 郁南县| 当阳市| 平湖市| 嘉义县| 永年县| 瑞金市| 丰台区| 澄城县| 衡南县| 嘉兴市| 天长市| 哈巴河县| 泾川县| 伊川县| 定远县| 正定县| 上蔡县| 大城县| 永安市| 贵南县| 淮南市| 新龙县| 邯郸县| 凌源市| 汤阴县| 甘泉县| 鹤庆县| 江川县| 奇台县| 阿图什市| 金湖县| 江华| 巢湖市| 上栗县| 延寿县| 石家庄市| 根河市| 江华| 鄱阳县| 同德县|