並發下的交易處理,並發交易處理

來源:互聯網
上載者:User

並發下的交易處理,並發交易處理

事務保障,是軟體行業必須要做的事情。很多金融公司,就是由於交易處理不得當而倒閉。

我們都知道,事務有四大特性:ACID。即:原子性、一致性、隔離性、持久性。


四大特性

原子性

事務是資料庫的邏輯單位,事務總包括的諸操作那麼全部執行,要麼都不執行;

一致性

職務執行的結果,必須使資料庫從一個一直狀態、變到另一個一直狀態。一致性與原子性緊密關聯。

隔離性

一個事務的執行,不能被其他事務幹擾

持久性

一個事務一旦提交,它對資料庫中的資料改變就應該是永久性的。

這就是事務的四大特性。


隔離等級

下面,我們來具體來說一說隔離性

我們都知道,事務控制的太嚴格,程式在並發訪問的情況下,會降低程式的效能。所以,人們總是想讓事務為效能做出讓步,那麼就分出了四中隔離等級:

為提交讀、提交讀、重複讀、序列化。

但是,由於隔離界別限制的程度不同,那麼就會產生髒讀、不可重複讀取、幻讀的情況。

1. 髒讀:髒讀就是指當一個事務正在訪問資料,並且對資料進行了修改,而這種修改還沒有提交到資料庫中,這時,另外一個事務也訪問這個資料,然後使用了這個資料。

2. 不可重複讀取:是指在一個事務內,多次讀同一資料。在這個事務還沒有結束時,另外一個事務也訪問該同一資料。那麼,在第一個事務中的兩次讀資料之間,由於第二個事務的修改,那麼第一個事務兩次讀到的的資料可能是不一樣的。這樣就發生了在一個事務內兩次讀到的資料是不一樣的,因此稱為是不可重複讀取。例如,一個編輯人員兩次讀取同一文檔,但在兩次讀取之間,作者重寫了該文檔。當編輯人員第二次讀取文檔時,文檔已更改。原始讀取不可重複。如果只有在作者全部完成編寫後編輯人員才可以讀取文檔,則可以 避免該問題。

       3.幻讀:是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的資料進行了修改,這種修改涉及到表中的全部資料行。同時,第二個事務也修改這個表中的資料,這種修改是向表中插入一行新資料。那麼,以後就會發生操作第一個事務的使用者發現表中還有沒有修改的資料行,就好象發生了幻覺一樣。例如,一個編輯人員更改作者提交的文檔,但當生產部門將其更改內容合并到該文檔的主複本時,發現作者已將未編輯的新材料添加到該文檔中。如果在編輯人員和生產部門完成對原始文檔的處理之前,任何人都不能將新材料添加到文檔中,則可以避免該問題。


       下面看資料庫事務的隔離等級,由低到高依次為未提交讀、提交讀、重複讀、序列化。

這四個層級可以逐個解決髒讀、不可重複讀取、幻讀這幾類問題。

 

√:可能出現    ×:不會出現

 

髒讀

不可重複讀取

幻讀

Read uncommitted

Read committed

×

Repeatable read

×

×

Serializable

×

×

×

 

注意:我們討論隔離等級的情境,主要是在多個事務並發的情況下,因此,接下來的講解都圍繞事務並發。

       未提交讀

公司發工資了,領導把5000元打到singo的帳號上,但是該事務並未提交,而singo正好去查看賬戶,發現工資已經到賬,是5000元整,非常高興。可是不幸的是,領導發現發給singo的工資金額不對,是2000元,於是迅速復原了事務,修改金額後,將事務提交,最後singo實際的工資只有2000元,singo空歡喜一場。

 

出現上述情況,即我們所說的髒讀,兩個並發的事務,“事務A:領導給singo發工資”、“事務B:singo查詢工資賬戶”,事務B讀取了事務A尚未提交的資料。

當隔離等級設定為Readuncommitted時,就可能出現髒讀,如何避免髒讀,請看下一個隔離等級。


       讀提交

singo拿著工資卡去消費,系統讀取到卡裡確實有2000元,而此時她的老婆也正好在網上轉賬,把singo工資卡的2000元轉到另一賬戶,並在singo之前提交了事務,當singo扣款時,系統檢查到singo的工資卡已經沒有錢,扣款失敗,singo十分納悶,明明卡裡有錢,為何......

出現上述情況,即我們所說的不可重複讀取,兩個並發的事務,“事務A:singo消費”、“事務B:singo的老婆網上轉賬”,事務A事先讀取了資料,事務B緊接了更新了資料,並提交了事務,而事務A再次讀取該資料時,資料已經發生了改變。

當隔離等級設定為Readcommitted時,避免了髒讀,但是可能會造成不可重複讀取。

大多數資料庫的預設層級就是Readcommitted,比如Sql Server , Oracle。如何解決不可重複讀取這一問題,請看下一個隔離等級。


       重複讀

當隔離等級設定為Repeatableread時,可以避免不可重複讀取。當singo拿著工資卡去消費時,一旦系統開始讀取工資卡資訊(即事務開始),singo的老婆就不可能對該記錄進行修改,也就是singo的老婆不能在此時轉賬。

雖然Repeatableread避免了不可重複讀取,但還有可能出現幻讀。

singo的老婆工作在銀行部門,她時常通過銀行內部系統查看singo的信用卡消費記錄。有一天,她正在查詢到singo當月信用卡的總消費金額(select sum(amount) from transaction where month =本月)為80元,而singo此時正好在外面胡吃海塞後在收銀台買單,消費1000元,即新增了一條1000元的消費記錄(insert transaction... ),並提交了事務,隨後singo的老婆將singo當月信用卡消費的明細列印到A4紙上,卻發現消費總額為1080元,singo的老婆很詫異,以為出現了幻覺,幻讀就這樣產生了。

註:Mysql的預設隔離等級就是Repeatableread。


       序列化

Serializable是最高的交易隔離等級,同時代價也花費最高,效能很低,一般很少使用,在該層級下,事務順序執行,不僅可以避免髒讀、不可重複讀取,還避免了幻像讀。


PS:大多數資料庫都是使用提交讀,作為預設的隔離等級,如Oracle、SqlServer。因為在資料量訪問的情況下,這種方式效能較好,同時防止了髒讀的情況發生,儘管有不可重複讀取的情況,但是在可承受的範圍內;也有一些資料採用重複讀,作為預設的隔離等級。如果採用預設配置,那麼使用MySql的效能會稍低一些。MySql隔離等級的預設配置實現,原理是資料訪問時加了讀寫鎖,並發讀取時,分別加鎖,但是只有第一個加鎖的事務,才能修改事務,其他事務不能修改,它避免了可重複讀的情況。序列化同樣是加鎖,但是它加的是獨佔鎖,無論哪個線程讀取到資料,立馬會將其霸佔,直至其操作完成。這種方式一致性高,但是並發性不好,很少使用。


事務的傳播特性

在開發中,我們一個action中,可能調用多個Service,那麼這種情況,是如何保證事務的呢?事務的傳播特性。下面我們來看看Spring事務的傳播特性:

            1.     PROPAGATION_REQUIRED:支援當前事務,如果當前沒有事務,就建立一個事務。這是最常見的選擇。
            2.     PROPAGATION_SUPPORTS:支援當前事務,如果當前沒有事務,就以非事務方式執行。
            3.     PROPAGATION_MANDATORY:支援當前事務,如果當前沒有事務,就拋出異常。
            4.     PROPAGATION_REQUIRES_NEW:建立事務,如果當前存在事務,把當前事務掛起。
            5.     PROPAGATION_NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
            6.     PROPAGATION_NEVER:以非事務方式執行,如果當前存在事務,則拋出異常。
            7.     PROPAGATION_NESTED:支援當前事務,新增Savepoint點,與當前事務同步提交或復原。


    具體解釋一下:
            1.     PROPAGATION_REQUIRED:加入當前正要執行的事務不在另外一個事務裡,那麼就起一個新的事務。比如說,ServiceB.methodB的事務層級定義為PROPAGATION_REQUIRED,那麼由於執行ServiceA.methodA的時候,ServiceA.methodA已經起了事務,這時調用ServiceB.methodB,ServiceB.methodB看到自己已經運行在ServiceA.methodA的事務內部,就不再起新的事務。而假如ServiceA.methodA啟動並執行時候發現自己沒有在事務中,他就會為自己分配一個事務。這樣,在ServiceA.methodA或者在ServiceB.methodB內的任何地方出現異常,事務都會被復原。即使ServiceB.methodB的事務已經被提交,但是ServiceA.methodA在接下來fail要復原,ServiceB.methodB也要復原。
           2.     PROPAGATION_SUPPORTS:如果當前在事務中,即以事務的形式運行,如果當前不再一個事務中,那麼就以非事務的形式運行。
           3.     PROPAGATION_MANDATORY:必須在一個事務中運行。也就是說,他只能被一個父事務調用。否則,他就要拋出異常。
           4.     PROPAGATION_REQUIRES_NEW:這個就比較繞口了。 比如我們設計ServiceA.methodA的事務層級為PROPAGATION_REQUIRED,ServiceB.methodB的事務層級為PROPAGATION_REQUIRES_NEW,那麼當執行到ServiceB.methodB的時候,ServiceA.methodA所在的事務就會掛起,ServiceB.methodB會起一個新的事務,等待ServiceB.methodB的事務完成以後,他才繼續執行。他與PROPAGATION_REQUIRED的事務區別在於事務的復原程度了。因為ServiceB.methodB是新起一個事務,那麼就是存在兩個不同的事務。如果ServiceB.methodB已經提交,那麼ServiceA.methodA失敗復原,ServiceB.methodB是不會復原的。如果ServiceB.methodB失敗復原,如果他拋出的異常被ServiceA.methodA捕獲,ServiceA.methodA事務仍然可能提交。
          5.     PROPAGATION_NOT_SUPPORTED:當前不支援事務。比如ServiceA.methodA的事務層級是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務層級是PROPAGATION_NOT_SUPPORTED ,那麼當執行到ServiceB.methodB時,ServiceA.methodA的事務掛起,而他以非事務的狀態運行完,再繼續ServiceA.methodA的事務。
          6.     PROPAGATION_NEVER:不能在事務中運行。假設ServiceA.methodA的事務層級是PROPAGATION_REQUIRED, 而ServiceB.methodB的事務層級是PROPAGATION_NEVER ,那麼ServiceB.methodB就要拋出異常了。
          7.     PROPAGATION_NEST:理解Nested的關鍵是savepoint。
    他與PROPAGATION_REQUIRES_NEW的區別是,PROPAGATION_REQUIRES_NEW另起一個事務,將會與他的父事務相互獨立, 而Nested的事務和他的父事務是相依的,他的提交是要等和他的父事務一塊提交的。也就是說,如果父事務最後復原,他也要復原的。而Nested事務的好處是他有一個savepoint。也就是說ServiceB.methodB失敗復原,那麼ServiceA.methodA也會復原到savepoint點上,ServiceA.methodA可以選擇另外一個分支,比如 ServiceC.methodC,繼續執行,來嘗試完成自己的事務。 但是這個事務並沒有在EJB標準中定義。


PS:我們最常用的傳播特性就是PROPAGATION_REQUIRED。支援當前事務,如果當前沒有事務,就建立一個事務。


分散式交易

1.XA

XA 是由X/Open組織提出的分散式交易的規範。XA規範主要定義了(全域)交易管理員(Transaction Manager)和(局部)資源管理員(Resource Manager)之間的介面。XA介面是雙向的系統介面,在交易管理員(Transaction Manager)以及一個或多個資源管理員(Resource Manager)之間形成通訊橋樑。XA之所以需要引入交易管理員是因為,在分布式系統中,從理論上講,兩台機器理論上無法達到一致的狀態,需要引入一個單點進行協調。交易管理員控制著全域事務,管理事務生命週期,並協調資源。資源管理員負責控制和管理實際資源(如資料庫或 JMS隊列)。

      2.JTA

作 為java平台上事務規範JTA(Java Transaction API)也定義了對XA事務的支援,實際上,JTA是基於XA架構上建模的,在JTA 中,交易管理員抽象為javax.transaction.TransactionManager介面,並通過底層事務服務(即JTS)實現。像很多其他 的java規範一樣,JTA僅僅定義了介面,具體的實現則是由供應商(如J2EE廠商)負責提供,目前JTA的實現主要由以下幾種:

1.J2EE容器所提供的JTA實現(JBoss)
2.獨立的JTA實現:如JOTM,Atomikos.這些實現可以應用在那些不使用J2EE應用伺服器的環境裡用以提供分布事事務保證。如Tomcat,Jetty以及普通的java應用。


       3.兩階段交易認可

所有關於分散式交易的介紹中都必然會講到兩階段交易認可,因為它是實現XA分散式交易的關鍵(確切地說:兩階段交易認可主要保證了分散式交易的原子性:即所有結點要麼全做要麼全不做)。所謂的兩個階段是指:第一階段:準備階段和第二階段:提交階段。



         1.準備階段: 事務協調者(交易管理員)給每個參與者(資源管理員)發送Prepare訊息,每個參與者要麼直接返回失敗(如許可權驗證失敗),要麼在本地執行事務,寫本 地的redo和undo日誌,但不提交,到達一種“萬事俱備,只欠東風”的狀態。(關於每一個參與者在準備階段具體做了什麼目前我還沒有參考到確切的資 料,但是有一點非常確定:參與者在準備階段完成了幾乎所有正式提交的動作,有的材料上說是進行了“試探性的提交”,只保留了最後一步耗時非常短暫的正式提 交操作給第二階段執行。)

        2.提交階段:如果協調者收到了參與者的失敗訊息或者逾時,直接給每個參與者發送復原(Rollback)訊息;否則,發送提交(Commit)訊息;參與者根據協調者的指令執行提交或者復原操作,釋放所有交易處理過程中使用的鎖資源。(注意:必須在最後階段釋放鎖資源)

        總結二次提交:
        將提交分成兩階段進行的目的很明確,就是儘可能晚地提交事務,讓事務在提交前儘可能地完成所有能完成的工作,這樣,最後的提交階段將是一個耗時極短的微小操 作,這種操作在一個分布式系統中失敗的機率是非常小的,也就是所謂的“網路通訊危險期”非常的短暫,這是兩階段交易認可確保分散式交易原子性的關鍵所在。(唯 一理論上兩階段交易認可出現問題的情況是當協調者發出提交指令後當機並出現磁碟故障等永久性錯誤,導致事務不可追蹤和恢複)


        從兩階段交易認可的工 作方式來看,很顯然,在提交事務的過程中需要在多個節點之間進行協調,而各節點對鎖資源的釋放必須等到事務最終提交時,這樣,比起一階段提交,兩階段交易認可 在執行同樣的事務時會消耗更多時間。事務執行時間的延長意味著鎖資源發生衝突的機率增加,當事務的並發量達到一定數量的時候,就會出現大量事務積壓甚至出 現死結,系統效能就會嚴重下滑。這就是使用XA事務

        4.一階段提交(Best Efforts 1PC模式)

不像兩階段交易認可那樣複雜,一階段提交非常直白,就是從應用程式向資料庫發出提交請求到資料庫完成提交或復原之後將結果返回給應用程式的過程。一階段提交不需 要“協調者”角色,各結點之間不存在協調操作,因此其事務執行時間比兩階段交易認可要短,但是提交的“危險期”是每一個事務的實際提交時間,相比於兩階段提 交,一階段提交出現在“不一致”的機率就變大了。但是我們必須注意到:只有當基礎設施出現問題的時候(如網路中斷,當機等),一階段提交才可能會出現“不 一致”的情況,相比它的效能優勢,很多團隊都會選擇這一方案。關於在spring環境下如何?一階段提交,有一篇非常優秀的文章值得參考:http://www.javaworld.com/javaworld/jw-01-2009/jw-01-spring-transactions.html?page=5

        5.事務補償機制

像best efforts 1PC這種模式,前提是應用程式能擷取所有的資料來源,然後使用同一個交易管理員(這裡指是的spring的交易管理員)管理事務。這種模式最典型的應用場 景非資料庫sharding莫屬。但是對於那些基於web service/rpc/jms等構建的高度自治(autonomy)的分布式系統介面,best efforts 1PC模式是無能為力的,此類情境下,還有最後一種方法可以協助我們實現“最終一致性”,那就是事務補償機制。關於事務補償機制是一個大話題,本文只簡單 提及,以後會作專門的研究和介紹。

        6.在基於兩階段交易認可的標準分散式交易和Best Efforts 1PC兩者之間如何選擇

一 般而言,需要互動的子系統數量較少,並且整個系統在未來不會或很少引入新的子系統且負載長期保持穩定,即無伸縮要求的話,考慮到開發複雜度和工作量,可以 選擇使用分散式交易。對於時間需求不是很緊,對效能要求很高的系統,應考慮使用Best Efforts 1PC或事務補償機制。對於那些需要進行sharding改造的系統,基本上不應再考慮分散式交易,因為sharding開啟了資料庫水平伸縮的視窗,使 用分散式交易看起來好像是為新開啟的視窗又加上了一把枷鎖。

總結一下

事務的控制,是程式編寫中必須進行的一步。我們編寫程式時,往往注意不到自己在使用事務。這是因為事務一般分為兩種方式:編程式事務和聲明式事務。編程式事務雖然很靈活,但是需要手動寫JDBC模板式的代碼來控制事務,所以我們不經常使用;我們經常使用聲明式事務,以AOP的方式切入程式中,完全是基於配置的,在代碼中沒有體現,所以我們會看不到事務代碼。

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.