本文中我不打算解釋EJB的復雜性。即將推出的EJB 3規范專門針對目標和開發模型,使得它更容易;該規范還提供依賴注入以及在實體bean容器之外的更容易的測試。相反,本文的目標在于提供BEA WebLogic Server 8.1和9.0中可用的高級選項的深度分析,使開發人員改善CMP bean的性能——在很多情況下可極大地改善。該主題很寬泛,不可能在一篇文章中一一涉及;因此,我只重點討論CMP實體bean的并發以及長期緩存策略。我還簡要說明了最新版本BEA WebLogic Server 9.0中的改進。
并發策略
J2EE開發人員知道EJB容器維護了一個實體bean緩存或者池,通??稍诓渴鹈枋龇信渲?。令人驚奇的是,相當多的J2EE開發人員不知道這并不意味著一旦J2EE服務器從數據庫中加載一個特定的bean實例,它就不再去數據庫中尋找該實例,因為該實例已經保存在緩存池中了。相反,默認情況下J2EE服務器執行ejbLoad()在每次事務的開始從數據庫中同步該實例的狀態?;旧?,CMP bean每運行一次(即使該bean在前一個事務中已經被加載),服務器就執行一次SQL select語句來刷新它。只有在一個事務中操作多個實體bean實例時,服務器才會緩存它們。
顯然,在每次事務中都重新從數據庫中加載狀態會造成很大的性能影響!這個默認行為很容易理解:如果數據庫被多個進程共享,并且每個進程都可以改變數據庫中持久對象的狀態,那么這將是最安全的方法。但是可以通過告訴J2EE服務器保留事務間實體bean的緩存實例,從而避免大部分時間里從數據庫中刷新數據來略微改善這種情況。為了解決這個問題并生成一個最優的解決方案,首先我將討論BEA WebLogic Server中可用的不同的并發策略。
對于EJB開發人員來說很重要的一點是要知道實體bean中可用的不同并發策略。令人驚奇的是,有的開發人員甚至不知道并發選項的存在。那么適用于實體bean的并發策略是什么呢?EJB容器是一個高度多線程的應用程序,同時響應來自多個客戶端的請求,這些請求通常會訪問同一資源,比如數據表中的一行。因此,EJB容器應該管理對實體bean實例的并發訪問;更加技術性地講,并發策略決定了容器如何以及何時將實體bean的每個實例與底層數據庫同步。
目前WebLogic Server中有四種可用的并發策略:排他、數據庫、樂觀和只讀。默認情況下,從7.0版本開始,WebLogic Server就使用的是數據庫并發。上面四種策略按性能從低到高依次排列。我將討論每種策略的優缺點。
排他性并發
排他性并發意味著容器最多為每個主要鍵值創建一個實體bean實例(比如,表中的一行映射到容器中的一個EJB實例)。對指定實例的訪問是串行的,并且請求是按照順序逐個執行的。這種策略有一些嚴重的問題。首先,性能由于多個客戶端對bean的串行訪問受到明顯影響,并且您不能再考慮應用程序的伸縮性。其次,EJB的單個實例(以及容器持有的關聯鎖)對于一個JVM(一個服務器實例)來說是本地的,不能在集群中工作。該策略只是用于后向兼容(早期版本的WebLogic Server默認使用它),應該盡量不用。
數據庫并發
數據庫并發策略是目前WebLogic Server版本中的默認并發策略。它提供了數據持久性和性能間的折中考慮。原理很簡單:WebLogic Server并不自己管理鎖,而是為每個試圖訪問該bean的每個事務創建一個新的bean實例,并將并發控制和死鎖檢測委派給底層數據庫。這就像多個客戶端對單個數據庫進行并行數據庫操作;數據庫的隔離水平和鎖定策略將規定哪些更新、選擇和插入會進行,按照何種順序,以及哪些(如果有的話)會失敗。直接好處是該策略在集群環境中的良好適用性——只要集群中的所有節點共享一個數據庫,EJB容器就不需要為數據同步細節而煩惱。
該策略明顯比排他性策略更具伸縮性,并且對于某些應用程序效果尤為出眾,但是也無法擺脫一些嚴重的性能限制。即使這樣,容器仍保持了一個實體bean實例池,并且這些實例不包含事務間的任何中間狀態。這是實例池化而不是緩存數據。池化無狀態實例的整體思想可能來自于早期的JVM實現,那時對象創建還是一項很昂貴的操作,并且從性能的角度來看緩存對象實例是有好處的。在現代的JVM中情況并非如此,因為大部分情況下對象的創建非???,但是由于該行為是EJB規范中描述的,所有供應商都應支持它。然而,當使用數據庫并發策略時,容器從緩存中取出“無狀態的”bean實例,并且必須執行一條SQL選擇操作以獲得最新數據并填充實例字段。
這種方法可能還不錯,因為我們不用擔心“不新鮮的”bean實例(當數據庫中的數據被從同一集群中的另一個節點或者從不同應用程序中更新時),但是性能也同樣受到明顯影響。您總是在每次事務的開始以一個額外的select操作結束,即使您只是打算更新bean中的數據而對之前的值并不感興趣。因此,在主要或僅是執行更新或插入操作的應用程序中使用實體bean意義不大——容器可能花大量時間做不必要的選擇操作,然后再拋棄數據。
排他性和數據庫并發策略至少存在一個共同問題:更新丟失的可能性??梢韵胂髢蓚€客戶端幾乎同時更新映射到一個實體bean的表中的同一條記錄。如果數據庫中沒有鎖,先完成的更新操作的結果會被其次完成的更新所覆蓋。這是否是可接受的結果取決于您的業務需求。更新丟失通常是不可接受或者不想要的;因此,應用程序需要某種機制來避免或檢測更新丟失的情況,并且有機會恢復。當應用程序部署再多個節點上時使用排他性策略將不能控制更新丟失問題。但是如我之前所述,您不應再考慮該該策略。
原文轉自:http://www.anti-gravitydesign.com