国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Java理論和實踐:理解 JTS?平衡安全性和性能

2019-11-18 14:11:39
字體:
來源:轉載
供稿:網友

  級別:中級

Brian Goetz(brian@quiotix.com)
首席顧問,Quiotix Corp
2002 年 5 月

在他的關于 JTS 的系列文章的第 1 和第 2 部分,Brian 講述了一些基礎知識,包括什么是事務以及 J2EE 容器如何使事務服務對 EJB 組件透明。盡管能夠以聲明的方式而不是編程的方式指定組件的事務性語義可以大大增強配置企業應用程序時的靈活性,但在裝配應用程序時做出不當的決定會削弱應用程序的性能和穩定性。在這最后一部分,Brian 討論了 J2EE 提供的用來治理事務劃分和隔離的工具和一些高效率地使用這些工具的指導。請單擊文章頂部或底部的討論,在討論論壇與作者和其他讀者分享您對本文的想法。
在本系列的第 1 部分(“An introdUCtion to transactions”)和第 2 部分(“The magic beind the scenes”)中,我們定義了什么是事務,列舉了事務的基本特性(PRoperty),并探討了 java 事務服務(Java Transaction Service)和 J2EE 容器如何合作為事務提供對 J2EE 組件的透明支持。在本文中,我們將討論事務的劃分和隔離這個主題。

為 EJB 組件定義事務劃分和隔離屬性(attribute)的職責由應用程序裝配人員來承擔。假如這些屬性設置不當,會對應用程序的性能、可伸縮性或容錯能力造成嚴重的后果。不幸的是,并沒有一種必須遵守的規則用于正確設置這些屬性,但有一些指導可以幫助我們在并發危險和性能危險之間找到一種平衡。

我們在第 1 部分中討論過,事務主要是一種異常處理機制。事務在程序中的用途與合法合同在日常業務中的用途相似:假如出了什么問題它們可以幫助恢復。但由于大多數時間內都沒實際發生什么錯誤,我們就希望能夠盡量減少它們的開銷以及對其余時間的占用。我們在應用程序中如何使用事務會對應用程序的性能和可伸縮性產生很大的影響。

事務劃分
J2EE 容器提供了兩種機制用來定義事務的起點和終點:bean 治理的事務和容器治理的事務。在 bean 治理的事務中,用 UserTransaction.begin() 和 UserTransaction.commit() 在 bean 方法中顯式開始和結束一個事務。另一方面,容器治理的事務提供了更多的靈活性。通過在裝配描述符中為每個 EJB 方法定義事務性屬性,您可以指定每個方法的事務性需求并讓容器確定何時開始和結束一個事務。無論在哪種情況下,構建事務的基本指導方針都是一樣的。

進來,出去
事務劃分的第一條規則是“盡量短小”。事務提供并發控制;這通常意味著資源治理器將代表您獲得您在事務期間訪問的數據項的鎖,并且它必須一直持有這些鎖,直到事務結束。(請回憶一下本系列第 1 部分所討論的 ACID 特性,其中“ACID”的“I”代表“隔離”(Isolation)。也就是說,一個事務的結果影響不到與該事務并發執行的其它事務。)當您擁有鎖時,任何需要訪問您鎖定的數據項的其它事務將不得不一直等待,直到您釋放鎖。假如您的事務很長,那些其它的所有事務都將被鎖定,您的應用程序吞吐量將大幅度下降。


規則 1:使事務盡可能短小。

通過使事務盡量短小,您可以把阻礙其它事務的時間縮到最短,從而提高應用程序的可伸縮性。保持事務盡可能短小的最好方法當然是不在事務中間做任何不必要耗費時間的事,非凡是不要在事務中間等待用戶輸入。

開始一個事務,從數據庫檢索一些數據,顯示數據,然后在仍處于事務中時請用戶做出一個選擇可能比較誘人。千萬別這么做!即使用戶注重力集中,也要花費數秒來響應 ? 而在數據庫中擁有鎖數秒的時間已經是很長的了。假如用戶決定離開計算機,或許是去吃午餐或者甚至回家一天,會發生什么情況?應用程序將只好無奈停機。在事務期間執行 I/O 是導致災難的秘訣。


規則 2:在事務期間不要等待用戶輸入。

將相關的操作歸在一起
由于每個事務都有不小的開銷,您可能認為最好是在單個事務中執行盡可能多的操作以使每個操作的開銷達到最小。但規則 1 告訴我們長事務對可伸縮性不利。那么如何實現最小化每個操作的開銷和可伸縮性之間的平衡呢?

我們把規則 1 設置為邏輯上的極端 ? 每個事務一個操作 ? 這樣不僅會導致額外開銷,還會危及應用程序狀態的一致性。假定事務性資源治理器維護應用程序狀態的一致性(請回憶一下第 1 部分,其中“ACID”的“C”代表“一致性”(Consistency)),但它們依靠應用程序來定義一致性的意思。實際上,我們在描述事務時使用的一致性的定義有點圓滑:應用程序說一致性是什么意思它就是什么意思。應用程序把幾組應用程序狀態的變化組織到幾個事務中,結果應用程序的狀態就成了定義上的(by definition)一致。然后資源治理器確保假如它必須從故障恢復的話,就把應用程序狀態恢復到最近的一致狀態。

在第 1 部分中,我們給出了一個在銀行應用程序中將資金從一個帳戶轉移到另一個帳戶的示例。清單 1 展示了這個示例可能的 SQL 實現,它包含 5 個 SQL 操作(一個選擇,兩個更新和兩個插入操作):

清單 1. 資金轉移的樣本 SQL 代碼
SELECT accountBalance INTO aBalance
FROM Accounts WHERE accountId=aId;
IF (aBalance >= transferAmount) THEN
UPDATE Accounts
SET accountBalance = accountBalance - transferAmount
WHERE accountId = aId;
UPDATE Accounts
SET accountBalance = accountBalance + transferAmount
WHERE accountId = bId;
INSERT INTO AccountJournal (accountId, amount)
VALUES (aId, -transferAmount);
INSERT INTO AccountJournal (accountId, amount)
VALUES (bId, transferAmount);
ELSE
FAIL "Insufficient funds in account";
END IF




假如我們把這個操作作為五個單獨的事務來執行會發生什么情況?這樣不僅會使執行速度變慢(由于事務開銷),還會失去一致性。例如,假如一個人從帳戶 A 取了錢,作為執行第一次 SELECT(檢查余額)和隨后的記入借方 UPDATE 之間的一個單獨事務的一部分,會發生什么情況?這樣會違反我們認為這段代碼會強制遵守的業務規則 ? 帳戶余額應該是非負的。假如在第一次 UPDATE 和第二次 UPDATE 之間系統失敗會發生什么情況?現在,當系統恢復時,錢已經離開了帳戶 A 但還沒有記入帳戶 B 的貸方,并且也無記錄說明原因。這樣,哪個帳戶的所有者都不會開心。

清單 1 中的五個 SQL 操作是單個相關操作 ? 將資金從一個帳戶轉移到另一個帳戶 ? 的一部分。因此,我們希望要么全部執行它們,要么一個也不執行,建議在單個事務中全部執行它們。


規則 3:將相關操作歸到單個事務中。

理想化的平衡
規則 1 說事務應盡可能短小。清單 1 中的示例表明有時候我們必須把一些操作歸到一個事務中來維護一致性。當然,它要依靠應用程序來確定“相關操作”是由什么組成的。我們可以把規則 1 和 3 結合在一起,提供一個描述事務范圍的一般指導,我們規定它為規則 4:


規則 4:把相關操作歸到單個事務中,但把不相關的操作放到單獨的事務中。

容器治理的事務
在使用容器治理的事務時,不是顯式聲明事務的起點和終點,而是為每個 EJB 方法定義事務性需求。bean 的 assembly-descriptor 的 container-transaction 部分的 trans-attribute 元素中定義了事務模式。(清單 2 中顯示了一個 assembly-descriptor 示例。)方法的事務模式以及狀態 ? 調用方法是否早已在事務中被征用 ? 決定了當 EJB 方法被調用時容器應該進行下面幾個操作中的哪一個:

征用現有事務中的方法。
創建一個新事務,并征用該事務中的方法。
不征用任何事務中的方法。
拋出一個異常。
清單 2. 樣本 EJB 裝配描述符
<assembly-descriptor>
...
<container-transaction>
<method>
<ejb-name>MyBean</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>MyBean</ejb-name>
<method-name>logError</method-name>
</method>
<trans-attribute>RequiresNew</trans-attribute>
</container-transaction>
...
</assembly-descriptor>




J2EE 規范定義了六種事務模式:Required、RequiresNew、Mandatory、Supports、NotSupported 和 Never。表 1 概述了每種模式的行為 ? 在現有事務中被調用和不在事務內調用時的行為 ? 并描述了每種模式受哪些類型的 EJB 組件支持。(一些容器可能答應您在選擇事務模式時有更多的靈活性,但這種使用要依靠特定于容器的功能,因此不適合跨容器的情況)。

表 1. 事務模式

事務模式 Bean 類型 在事務 T 內被調用時的行為 在事務外被調用時的行為
Required 會話、實體、消息驅動 在 T 中征用 新建事務
RequiresNew 會話、實體 新建事務 新建事務
Supports 會話、消息驅動 在 T 中征用 不帶事務運行
Mandatory 會話、實體 在 T 中征用 出錯
NotSupported 會話、消息驅動 不帶事務運行 不帶事務運行
Never 會話、消息驅動 出錯 不帶事務運行

在只使用容器治理的事務的應用程序中,只有組件調用事務模式為 Required 或 RequiresNew 的 EJB 方法時才啟動事務。假如容器創建一個事務作為調用事務性方法的結果,當該方法完成時將關閉該事務。假如方法正常返回,容器將提交事務(除非應用程序已經要求回滾事務)。假如方法通過拋出一個異常退出,容器將回滾事務并傳播該異常。假如在現有事務 T 中調用了一個方法,并且事務模式指定應該不帶事務運行該方法或者在新事務中運行該方法,那么事務 T 將被暫掛,一直到方法完成,然后先前的事務 T 被恢復。

選擇一種事務模式
那么我們應該為自己的 bean 方法選擇哪種模式呢?對于會話 bean 和消息驅動 bean,您通常想使用 Required 來確保每個調用都被作為事務的一部分執行,但仍將答應方法作為一個更大的事務的組件。請小心使用 RequiresNew;只有在確定自己的方法的行為應該與調用您的方法的行為分開提交時,才應該使用這種模式。RequiresNew 一般情況下只和與系統中其它對象關系很少或沒什么關系的對象(比如日志對象)一起使用。(把 RequiresNew 與日志對象一起使用比較有意義,因為您可能希望在不管外圍事務是否提交的情況下提交日志消息。)

RequiresNew 使用不當會導致與上面的描述相似的情況,其中,清單 1 中的代碼在五個分開的事務而不是一個事務中執行,這樣會使應用程序處于不一致狀態。

對于 CMP(容器治理的持久性,container-managed persistence)實體 bean,通常是希望使用 Required。Mandatory 也是一個合理的選項,非凡是在最初開發時;這將會警告您實體 bean 方法在事務外被調用這種情況,這時可能會指出一個部署錯誤。您幾乎從不希望把 RequiresNew 和 CMP 實體 bean 一起使用。NotSupported 和 Never 旨在用于非事務性資源,比如 Java 事務 API(Java Transaction API,JTA)事務中無法征用的外部非事務性系統或事務性系統的適配器。

假如 EJB 應用程序設計得當,應用上面的事務模式指導往往會自然地產生規則 4 建議的事務劃分。原因是 J2EE 體系架構鼓勵把應用程序分解為最小的方便處理的塊,并且每個塊都作為一個單獨的請求被處理( 不管是以 HTTP 請求的形式還是作為在 JMS 隊列中排隊的消息的結果)。

重溫隔離
在第 1 部分中,我們定義了隔離(isolation)的意思是:一個事務的影響對與該事務并發執行的其它事務是不可見的;從事務的角度來看,好象事務是連續執行而非并行執行。盡管事務性資源治理器經常可以同時處理許多事務并提供隔離的假象,但有時隔離限制實際上要求把新事務延遲到現有事務完成后才開始。由于完成一個事務至少包括一個同步磁盤 I/O(寫到事務日志),這就會把每秒的事務數限制到接近每秒的寫磁盤次數,這對可伸縮性不利。

實際上,通常是充分放松隔離需求以答應更多的事務并發執行并使系統響應能夠得到改善,使可伸縮性變得更強。幾乎所有的數據庫都支持標準隔離級別:讀未提交的(Read Uncommitted)、讀已提交的(Read Committed)、可重復的讀(Repeatable Read) 和可串行化的(Serializable)。

不幸的是,為容器治理的事務治理隔離目前是在 J2EE 規范的范圍之外。但是,許多 J2EE 容器,比如 IBM WebSphere 和 BEA WebLogic,將提供特定于容器的擴展,這些擴展答應您以每方法(per-method)為基礎設置事務隔離級別,設置方法與在裝配描述符中設置事務模式的方法相同。對于 bean 治理的事務,您可以通過 JDBC 或者其它資源治理器連接設置隔離級別。

為闡明隔離級別之間的差異,我們首先把幾個并發危險分類 ? 這幾種危險是當沒有適當地隔離時一個事務可能會干涉另一個事務的情況。下列的所有這些危險都與這種情況( 第二個事務已經啟動后第一個事務變得對第二個事務可見)的結果有關:

臟讀(Dirty Read):當一個事務的中間(未提交的)結果對另一個事務可見時就會發生這種情況。


不可重復的讀(Unrepeatable Read):當一個事務讀取一個數據項,然后重新讀取這個數據項并看到不同的值時就是發生了這種情況。


虛讀(Phantom Read):當一個事務執行返回多個行的查詢,稍后再次執行同一個查詢并看到第一次執行該查詢沒出現的額外行時就是發生了這種情況。
四個標準隔離級別與這三個隔離危險相關,如表 2 所示。最低的隔離級別“讀未提交的”并不能保護事務不被其它事務更改,但它的速度最快,因為它不需要爭奪讀鎖。最高的隔離級別“可串行化的”與上面給出的隔離的定義相當;每個事務好象都與其它事務的影響完全隔離。

表 2. 事務隔離級別

隔離級別 臟讀 不可重復的讀 虛讀
讀未提交的 是 是 是
讀已提交的 否 是 是
可重復的讀 否 否 是
可串行化的 否 否 否

對于大多數數據庫,缺省的隔離級別為“讀已提交的”,這是個很好的缺省選擇,因為它阻止事務在事務中的任何給定的點看到應用程序數據的不一致視圖。“讀已提交的”是一個很不錯的隔離級別,用于大多數典型的短事務,比如獲取報表數據或獲取要顯示給用戶的數據的時候(多半是作為 Web 請求的結果),也用于將新數據插入到數據庫的情況。

當您需要所有事務間有較高級別的一致性時,使用較高的隔離級別“可重復的讀”和“可串行化的”比較合適,比如在清單 1 示例中,您希望從檢查余額以確保有足夠的資金到您實際取錢期間賬戶余額一直保持不變;這就要求至少要用“可重復的讀”隔離級別。在數據一致性絕對重要的情況下,比如審核記帳數據庫以確保一個帳戶的所有借方金額和貸方金額的總數等于它目前的余額時,可能還需要防止創建新行。這種情況下就需要使用“可串行化的”隔離級別。

最低的隔離級別“讀未提交的”很少使用。它適用于您只需要獲得近似值,否則查詢將導致您不希望的性能開銷這種情況。當您想要估計一個變化很快的數量,如定單數或者今天所下定單的總金額(以美元為單位)時一般使用““讀未提交的”。

因為隔離和可伸縮性之間實際是一種此消彼長的關系,所以您在為事務選擇隔離級別時應該小心行事。選擇太低的級別對數據比較危險。選擇太高的級別可能對性能不利,盡管負載比較輕時可能不會這樣。一般來說,數據一致性問題比性能問題更嚴重。假如拿不準,應該以小心為主,選擇一個較高的隔離級別。這就引出了規則 5:


規則 5:使用保證數據安全的最低隔離級別,但假如拿不準,請使用“可串行化的”。

即使您打算剛開始時以小心為主并希望結果性能可以接受 ?(被稱為“拒絕和祈禱(denial and prayer)”的性能治理技術 ? 很可能是最常用的性能策略,盡管大多數開發者都不承認這一點),在開發組件時考慮隔離需求也是有利的。您應該努力編寫能夠容忍級別較低但實用的隔離級別的事務,這樣,當稍后性能成為問題時,自己就不會陷入困境。因為您需要知道方法正在做什么以及這個方法中隱藏了什么一致性假設來正確設置隔離級別,那么在開發期間仔細說明并發需求和假設,以便在裝配應用程序時幫助作出正確的決定也不失為一個好主意。

結束語
本文中提供的許多指導可能看起來有點互相矛盾,因為象事務劃分和隔離這種問題本來就是此消彼長的。我們正在努力平衡安全性(假如我們不關心安全性,那就壓根不必用事務了)和我們用來提供安全限度的工具的性能開銷。正確的平衡要依靠許多因素,包括與系統故障或當機時間相關的代價或損害以及組織的風險承受能力。

參考資料

請單擊文章頂部或底部的討論參與本文的討論論壇。


JTS 系列的第 1 部分(“An introduction to transactions”)講述了一些基礎知識,包括什么是事務,以及事務對于構建可靠的分布式應用程序來說至關重要的原因。第 2 部分(“The magic beind the scenes”)探討了 Java 事務服務(Java Transaction Service)和 J2EE 如何合作為事務提供對 J2EE 組件的透明支持。


閱讀 Brian Goetz 的所有 Java 理論和實踐專欄。


Jim Grey 和 Andreas Reuter 合著的 Transaction Processing: Concepts and Techniques 是關于事務處理這個主題的權威著作。


Philip Bernstein 和 Eric Newcomer 合著的 Principles of Transaction Processing 是關于這個主題的很好的介紹;它包含了許多歷史和概念。


Ed Roman、Tyler Jewell 和 Scott Ambler 合著的 Mastering Enterprise JavaBeans 是關于 J2EE 和 EJB 技術的很好的介紹。


通過 Transaction Management under J2EE 1.2(JavaWorld,2000 年 7 月)學習聲明式事務劃分的基礎知識。


來自 WebSphere 開發者園地的這篇技術記要量化減少隔離級別的好處。


白皮書“WebSphere application Server Development Best Practices for Performance and Scalability”描述了用來治理 J2EE 應用程序性能和可伸縮性的幾種很有用的技術。


Visual Age 開發者園地上發表了一篇文章,這篇文章檢查了 J2EE 應用程序中的數據庫訪問和并發治理問題。


PreciseJava 中的教程“Best practices to improve performance in JDBC”描述了幾種有用的數據庫應用程序性能最優化。


請在 developerWorks Java 技術專區中查找其它關于 Java 技術的內容。

關于作者
Brian Goetz 是一位軟件顧問,在過去 15 年間一直從事專業軟件開發。他是 Quiotix,一家位于加利福尼亞,洛斯拉圖斯(Los Altos)的軟件開發和咨詢公司的總顧問。請參閱流行的業界出版物中 Brian 已經發表和即將發表的文章。您可以通過 brian@quiotix.com 與 Brian 聯系。

--摘自IBM developerWorks Java網站技術專區 .

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 剑川县| 兰坪| 峡江县| 荣昌县| 连江县| 仁化县| 新丰县| 前郭尔| 同心县| 武隆县| 建平县| 中超| 靖远县| 芷江| 朝阳县| 南岸区| 屏边| 洪洞县| 封丘县| 富宁县| 会昌县| 凤台县| 孙吴县| 磐安县| 济宁市| 静乐县| 司法| 元阳县| 翁源县| 原平市| 深圳市| 南和县| 关岭| 宜良县| 垦利县| 枣阳市| 临朐县| 南陵县| 莫力| 芦山县| 汝南县|