事務屬性小結


在NoSql和內存數據庫如此流行的今天,在談關系型數據庫的貌似有點落伍了,不過在傳統軟件行業和對數據一致性和安全性要求比較高的行業,關系型數據庫還是比較普遍的。正好最近看到一個數據庫事務相關的知識,自己在這幾年的工作中用的比較多,也在事務上面犯過很多的錯誤,正好借這個機會整理以下。

 

事務的ACID屬性

A(Atomicity)原子性: 在一個事務上下文里面,對數據庫進行的任何操作,必須保證是原子的,也就是說要么不做,要么全部都做,不能只做一部分。比如insert一條數據和delete一條數據,不知能只做insert操作而不做delete操作

 

C(Consistency)一致性:在事務的處理過程中,數據庫必須時刻要避免被置於不一致 (inconsistent)的狀態。這意味着在事務期間,每次對數據庫實施的插入、更新或刪除操作 時,數據庫的完整性約束(integrity constraints)都要得到保證,即使在事務還未被提交時 也必須如此。比如非空約束。

 

I(Isolation)隔離性:兩個不同的事務相互之間是彼此隔離程度。有一種說法是事務之間是彼此隔離的,一個事務不能夠讀取另一個事務未提交的數據,這個不太准確,這個屬於事務的隔離級別。wiki上的解釋是“

The isolation property ensures that the concurrent execution of transactions results in a system state that could have been obtained if transactions are executed serially, i.e. one after the other“

 

D(Durability)持久性:事務一旦提交,在事務中對數據庫進行的修改也就進行持久化存儲了,不會由於系統故障導致提交后的數據丟失。

 

ACID是關系型數據庫最重要的特性。

 

事務的隔離級別

這個和ACID中的隔離性有關,主要分為四個級別

1.         讀未提交(Read UnCommited)

這個是最弱的隔離級別,不滿足ACID中的隔離性的要求,大多數數據庫並不提供這個支持。見下面的例子:

 1

由於事務B讀取了事務未提交的數據,一旦回滾,事務B讀取的數據就是有問題的。

 

2.         讀已提交(Read Commited)

這個級別不允許事務B讀取事務A還未提交的 update操作更新后的數據,但是對於事務A的insert操作,在未提交之前,對事務B還是可見的。這就可能出現幻讀。見下面的例子

 2 1

 

這里面不會出現臟讀,保證事務B讀取的都是事務A update提交之后的數據。但是對於insert操作,就可能存在臟讀的問題,例如下面一個例子

 2 2

數據庫里面存在ABC這一條記錄,事務A新增加了XYZ這一條記錄,如果事務A正常提交,事務B讀取的結果沒有問題,如果事務A回滾,那么事務B就出現的臟讀,多了XYZ這一條記錄。

 

記得曾經自己在這個上面弄了一個bug,大概的業務邏輯是:先根據批日期,查詢某一天的總數據量,然后在查詢明細數據,生成到文件里面,然后更新狀態,這些都在一個事務里面。事務的隔離級別是默認的。當初發現查詢的總數據量和生成的明細數據數據不一致,原來在事務里面,還會有其他的事務往表里面插入數據,無論是否最終提交,都會被查詢出來,生成到文件里面。

 

  

3.         可重復讀(Repeatable Read)

這個隔離級別對於insert的數據提交之后才對另外一個事務可見,不會存在幻讀的情況。

3 1

但是事務之間還是會存在互相影響的情況,見下面的例子

 3 2

事務A和事務B都是先讀取Price的價格,然后在價格上面減去一定的數值,我們期望結果是70,但是實際結果可能是90,也可能是80。

 

4.         可序列化

事務只能順序的讀取數據,當一個事務在讀取和修改數據的時候,另外一個事務只能掛起,直到正在讀取和修改數據的事務提交之后,掛起的事務才能執行。

4

 

 

 

事務的隔離級別與並發性,一致性之間存在關系,隔離級別越高,並發性就越低,一致性就越高

 

這幾種隔離級別並不是每個數據庫都會提供,oracle沒有提供第一種隔離級別“讀未提交數據”,一般情況下,大多數數據庫的默認隔離級別就是“讀提交數據”。

這些隔離級別都是定義在java.sql. Connection中,在獲取連接的時候我們可以進行設置,但是一般情況下,系統會用數據庫默認的級別來設置。

 

Connection.TRANSACTION_READ_UNCOMMITTED;

Connection.TRANSACTION_READ_COMMITTED;

Connection.TRANSACTION_REPEATABLE_READ;  

Connection.TRANSACTION_SERIALIZABLE;

 

聲明式編程中事務的屬性

我們在使用spring活着ejb聲明式編程的時候,還會接觸到事務的傳播屬性,在spring的 TransactionDefinition 類里面進行定義。具體有以下幾個數值

 

Required(需要)

Mandatory(強制必須)

RequiresNew(需要新的)

Supports(支持)

NotSupported(不支持)

Never(不用)

 

 

Required:當前方法必須要求開啟事務,如果當前線程不存在事務,則開啟新的事務,如果當前線程已經存在事務,就加入到當前事務。這個是經常使用的。但是要注意的就是一旦事務中某一個方法回滾,當前事務上下文里面所有的操作都回滾,考慮到下面一個例子:

methodA{  

    read A =100  ;    

If(methodB()){

       A = A + 1;  

}else{

   A = A - 1;  

}

Update A;

Commit;

}

 

 

methodB{

  read B ;

  B = B – 1

  Update B;

 

  If(B>0){

Commit;

Return ture;

  }else{

     Rollback

Return false;

  }

}

 

 

假設A和B的事務都是Required,那么當調用MethodA的時候,如果method回滾了,對A的修改也就回滾了。所以上面的代碼不會達到預期的結果,也就是說A不可能修改成為99。

 

 

Required New:當前方法必須要求開啟新的事務,如果當前線程已經存在事務上下文,就暫停當前事務,等到新事務結束之后,在繼續恢復之前的事務。就拿上面的例子來說,methodB的對事務的修改不會影響到methodA。兩個事務之間不會互相影響。經常可以用到的場景就是在業務發生異常的時候發送短消息。如果業務發生異常,業務回滾,但是由於發送段消息是新的事務,不會受到業務異常的影響。

 

Mondary:當前方法必須要求事務,如果當前線程不存在事務,就拋出異常,如果存在,就加入到事務里。

 

 

Support:當前方法支持事務,如果當前線程存在事務,就加入到事務中去,如果不存在,不做任何操作。

 

Not Support:當前方法不支持事務,如果當前線程存在事務,就掛起當前事務,執行完當前方法,恢復事務。一般情況下在查詢的時候使用,如果一個方法只是查詢,並且非常耗時,就可以使用Not Support,避免事務時間超長。

 

Never:當前方法不支持事務,如果當前線程存在事務,則拋出異常。這種用的比較少。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM