<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Vincent

    Vicent's blog
    隨筆 - 74, 文章 - 0, 評論 - 5, 引用 - 0
    數(shù)據(jù)加載中……

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

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

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

    事務(wù)劃分

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

    進來,出去

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

    規(guī)則 1:使事務(wù)盡可能短小。

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

    開始一個事務(wù),從數(shù)據(jù)庫檢索一些數(shù)據(jù),顯示數(shù)據(jù),然后在仍處于事務(wù)中時請用戶做出一個選擇可能比較誘人。千萬別這么做!即使用戶注意力集中,也要花費數(shù)秒來響應(yīng) ― 而在數(shù)據(jù)庫中擁有鎖數(shù)秒的時間已經(jīng)是很長的了。如果用戶決定離開計算機,或許是去吃午餐或者甚至回家一天,會發(fā)生什么情況?應(yīng)用程序?qū)⒅缓脽o奈停機。在事務(wù)期間執(zhí)行 I/O 是導(dǎo)致災(zāi)難的秘訣。

    規(guī)則 2:在事務(wù)期間不要等待用戶輸入。

    將相關(guān)的操作歸在一起

    由于每個事務(wù)都有不小的開銷,您可能認為最好是在單個事務(wù)中執(zhí)行盡可能多的操作以使每個操作的開銷達到最小。但規(guī)則 1 告訴我們長事務(wù)對可伸縮性不利。那么如何實現(xiàn)最小化每個操作的開銷和可伸縮性之間的平衡呢?

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

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


    清單 1. 資金轉(zhuǎn)移的樣本 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
    
    												
    										

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

    清單 1 中的五個 SQL 操作是單個相關(guān)操作 ― 將資金從一個帳戶轉(zhuǎn)移到另一個帳戶 ― 的一部分。因此,我們希望要么全部執(zhí)行它們,要么一個也不執(zhí)行,建議在單個事務(wù)中全部執(zhí)行它們。

    規(guī)則 3:將相關(guān)操作歸到單個事務(wù)中。

    理想化的平衡

    規(guī)則 1 說事務(wù)應(yīng)盡可能短小。清單 1 中的示例表明有時候我們必須把一些操作歸到一個事務(wù)中來維護一致性。當(dāng)然,它要依賴應(yīng)用程序來確定“相關(guān)操作”是由什么組成的。我們可以把規(guī)則 1 和 3 結(jié)合在一起,提供一個描述事務(wù)范圍的一般指導(dǎo),我們規(guī)定它為規(guī)則 4:

    規(guī)則 4:把相關(guān)操作歸到單個事務(wù)中,但把不相關(guān)的操作放到單獨的事務(wù)中。





    回頁首


    容器管理的事務(wù)

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

    • 征用現(xiàn)有事務(wù)中的方法。
    • 創(chuàng)建一個新事務(wù),并征用該事務(wù)中的方法。
    • 不征用任何事務(wù)中的方法。
    • 拋出一個異常。

    清單 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 規(guī)范定義了六種事務(wù)模式: RequiredRequiresNewMandatorySupportsNotSupportedNever 。表 1 概述了每種模式的行為 ― 在現(xiàn)有事務(wù)中被調(diào)用和不在事務(wù)內(nèi)調(diào)用時的行為 ― 并描述了每種模式受哪些類型的 EJB 組件支持。(一些容器可能允許您在選擇事務(wù)模式時有更多的靈活性,但這種使用要依賴特定于容器的功能,因此不適合跨容器的情況)。

    表 1. 事務(wù)模式

    事務(wù)模式 Bean 類型 在事務(wù) T 內(nèi)被調(diào)用時的行為 在事務(wù)外被調(diào)用時的行為
    Required 會話、實體、消息驅(qū)動 在 T 中征用 新建事務(wù)
    RequiresNew 會話、實體 新建事務(wù) 新建事務(wù)
    Supports 會話、消息驅(qū)動 在 T 中征用 不帶事務(wù)運行
    Mandatory 會話、實體 在 T 中征用 出錯
    NotSupported 會話、消息驅(qū)動 不帶事務(wù)運行 不帶事務(wù)運行
    Never 會話、消息驅(qū)動 出錯 不帶事務(wù)運行

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

    選擇一種事務(wù)模式

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

    RequiresNew 使用不當(dāng)會導(dǎo)致與上面的描述相似的情況,其中,清單 1 中的代碼在五個分開的事務(wù)而不是一個事務(wù)中執(zhí)行,這樣會使應(yīng)用程序處于不一致狀態(tài)。

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

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





    回頁首


    重溫隔離

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

    實際上,通常是充分放松隔離需求以允許更多的事務(wù)并發(fā)執(zhí)行并使系統(tǒng)響應(yīng)能夠得到改善,使可伸縮性變得更強。幾乎所有的數(shù)據(jù)庫都支持標(biāo)準(zhǔn)隔離級別:讀未提交的(Read Uncommitted)、讀已提交的(Read Committed)、可重復(fù)的讀(Repeatable Read) 和可串行化的(Serializable)。

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

    為闡明隔離級別之間的差異,我們首先把幾個并發(fā)危險分類 ― 這幾種危險是當(dāng)沒有適當(dāng)?shù)馗綦x時一個事務(wù)可能會干涉另一個事務(wù)的情況。下列的所有這些危險都與這種情況( 第二個事務(wù)已經(jīng)啟動后第一個事務(wù)變得對第二個事務(wù) 可見)的結(jié)果有關(guān):

    • 臟讀(Dirty Read):當(dāng)一個事務(wù)的中間(未提交的)結(jié)果對另一個事務(wù)可見時就會發(fā)生這種情況。
    • 不可重復(fù)的讀(Unrepeatable Read):當(dāng)一個事務(wù)讀取一個數(shù)據(jù)項,然后重新讀取這個數(shù)據(jù)項并看到不同的值時就是發(fā)生了這種情況。
    • 虛讀(Phantom Read):當(dāng)一個事務(wù)執(zhí)行返回多個行的查詢,稍后再次執(zhí)行同一個查詢并看到第一次執(zhí)行該查詢沒出現(xiàn)的額外行時就是發(fā)生了這種情況。

    四個標(biāo)準(zhǔn)隔離級別與這三個隔離危險相關(guān),如表 2 所示。最低的隔離級別“讀未提交的”并不能保護事務(wù)不被其它事務(wù)更改,但它的速度最快,因為它不需要爭奪讀鎖。最高的隔離級別“可串行化的”與上面給出的隔離的定義相當(dāng);每個事務(wù)好象都與其它事務(wù)的影響完全隔離。

    表 2. 事務(wù)隔離級別

    隔離級別 臟讀 不可重復(fù)的讀 虛讀
    讀未提交的
    讀已提交的
    可重復(fù)的讀
    可串行化的

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

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

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

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

    規(guī)則 5:使用保證數(shù)據(jù)安全的最低隔離級別,但如果拿不準(zhǔn),請使用“可串行化的”。

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





    回頁首


    結(jié)束語

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

    posted on 2006-08-24 17:38 Binary 閱讀(206) 評論(0)  編輯  收藏 所屬分類: j2se

    主站蜘蛛池模板: 久久精品国产亚洲AV高清热| 亚洲国产精品不卡在线电影| 亚洲av无一区二区三区| 国产卡一卡二卡三免费入口| 亚洲精品午夜在线观看| 国产成人精品免费午夜app| 亚洲制服丝袜一区二区三区| 国产一卡二卡3卡四卡免费| 亚洲国产午夜电影在线入口| 国产麻豆视频免费观看| 亚洲精品乱码久久久久蜜桃| 国产免费观看黄AV片| 美女裸免费观看网站| 亚洲日韩欧洲无码av夜夜摸| 日韩视频在线观看免费| 亚洲国产福利精品一区二区| 成年在线网站免费观看无广告| 亚洲av无码一区二区三区天堂| 亚洲国产成人久久精品99| 免费网站观看WWW在线观看| 麻豆亚洲av熟女国产一区二| 成人免费777777| 国产va免费精品| 蜜芽亚洲av无码精品色午夜| 国产香蕉九九久久精品免费| 免费精品国自产拍在线播放 | 免费视频爱爱太爽了| 亚洲熟妇无码八V在线播放| 大胆亚洲人体视频| 在线免费不卡视频| 免费看又黄又爽又猛的视频软件| 国产成人亚洲综合无码精品| 在线视频免费观看高清| 青青草国产免费国产是公开| 亚洲AV无码成人精品区天堂| 亚洲人成电影网站免费| gogo免费在线观看| 波多野结衣亚洲一级| 亚洲欧洲国产成人综合在线观看 | 亚洲精品午夜国产va久久| 亚洲一级特黄无码片|