天天看點

spring事務之事務傳播機制和隔離級别Spring事務傳播行為隔離級别不可重複讀和幻讀的差別

Spring事務傳播行為

運用Spring事務,必須要深入了解它的傳播機制,否則會遇到各種意想不到的坑,Spring定義了七種傳播行為。

public interface TransactionDefinition {
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
}
           

含義如下:

傳播行為 官方含義 簡單了解
PROPAGATION_REQUIRED 表示目前方法必須在一個事務中運作。如果一個現有事務正在進行中,該方法将在那個事務中運作,否則就要開始一個新事務 有事務就用已有的,沒有就重新開啟一個
PROPAGATION_SUPPORTS 表示目前方法不需要事務性上下文,但是如果有一個事務已經在運作的話,它也可以在這個事務裡運作 有事務就用已有的,沒有也不會重新開啟
PROPAGATION_MANDATORY 表示該方法必須運作在一個事務中。如果目前沒有事務正在發生,将抛出一個異常 必須要有事務,沒事務抛異常
PROPAGATION_REQUIRES_NEW 表示目前方法必須在它自己的事務裡運作。一個新的事務将被啟動,而且如果有一個現有事務在運作的話,則将在這個方法運作期間被挂起 開啟新事務,若目前已有事務,挂起目前事務
PROPAGATION_NOT_SUPPORTED 表示該方法不應該在一個事務中運作。如果一個現有事務正在進行中,它将在該方法的運作期間被挂起 不需要事務,若目前已有事務,挂起目前事務
PROPAGATION_NEVER 表示目前的方法不應該在一個事務中運作。如果一個事務正在進行,則會抛出一個異常 不需要事務,若目前已有事務,抛出異常
PROPAGATION_NESTED 表示如果目前正有一個事務在進行中,則該方法應當運作在一個嵌套式事務中。被嵌套的事務可以獨立于封裝事務進行送出或復原。如果封裝事務不存在,行為就像PROPAGATION_REQUIRES一樣 嵌套事務,如果外部事務復原,則嵌套事務也會復原!!!外部事務送出的時候,嵌套它才會被送出。嵌套事務復原不會影響外部事務。

Spring的@Transactional預設是PROPAGATION_REQUIRED。

隔離級别

隔離級别轉載自http://www.javaseo.cn/article/88/

Spring的事務隔離級别最終還是交給資料庫處理,Spring并沒有做特殊處理。

  1. Read uncommitted 讀未送出

    公司發工資了,把50000元打到我的賬号上,但是該事務并未送出,而我正好去檢視賬戶,發現工資已經到賬,是50000元整,非常高興。可是不幸的是,上司發現發給的工資金額不對,是2000元,于是迅速復原了事務,修改金額後,将事務送出,最後我實際的工資隻有2000元,空歡喜一場。

    髒讀是兩個并發的事務,“事務A:上司發工資”、“事務B:我查詢工資賬戶”,事務B讀取了事務A尚未送出的資料。

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

  2. Read committed 讀送出

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

    不可重複讀是兩個并發的事務,“事務A:消費”、“事務B:老婆網上轉賬”,事務A事先讀取了資料,事務B緊接了更新了資料,并送出了事務,而事務A再次讀取該資料時,資料已經發生了改變。

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

  3. Repeatable read 重複讀

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

    雖然Repeatable read避免了不可重複讀,但還有可能出現幻讀。例如:老婆工作在銀行部門,她時常通過銀行内部系統檢視我的信用卡消費記錄。有一天,她正查詢到我當月信用卡的總消費金額(select sum(amount) from transaction where month = 本月)為80元,而我此時正好在外面吃完大餐後在收銀台買單,消費1000元,即新增了一條1000元的消費記錄(insert transaction ... ),并送出了事務,随後老婆将我的當月信用卡消費的明細列印到A4紙上,卻發現消費總額為1080元,老婆很詫異,以為出現了幻覺,幻讀就這樣産生了。

  4. Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生

不可重複讀和幻讀的差別

當然,   從總的結果來看,   似乎兩者都表現為兩次讀取的結果不一緻.

但如果你從控制的角度來看,   兩者的差別就比較大

對于前者,   隻需要鎖住滿足條件的記錄

對于後者,   要鎖住滿足條件及其相近的記錄

-----------------------------------------------------------

我這麼了解是否可以?

避免不可重複讀需要鎖行就行

避免幻影讀則需要鎖表

------------------------

####不可重複讀和幻讀的差別####

很多人容易搞混不可重複讀和幻讀,确實這兩者有些相似。但不可重複讀重點在于update和delete,而幻讀的重點在于insert。

如果使用鎖機制來實作這兩種隔離級别,在可重複讀中,該sql第一次讀取到資料後,就将這些資料加鎖,其它事務無法修改這些資料,就可以實作可重複 讀了。但這種方法卻無法鎖住insert的資料,是以當事務A先前讀取了資料,或者修改了全部資料,事務B還是可以insert資料送出,這時事務A就會 發現莫名其妙多了一條之前沒有的資料,這就是幻讀,不能通過行鎖來避免。需要Serializable隔離級别 ,讀用讀鎖,寫用寫鎖,讀鎖和寫鎖互斥,這麼做可以有效的避免幻讀、不可重複讀、髒讀等問題,但會極大的降低資料庫的并發能力。

是以說不可重複讀和幻讀最大的差別,就在于如何通過鎖機制來解決他們産生的問題。

上文說的,是使用悲觀鎖機制來處理這兩種問題,但是MySQL、ORACLE、PostgreSQL等成熟的資料庫,出于性能考慮,都是使用了以樂觀鎖為理論基礎的MVCC(多版本并發控制)來避免這兩種問題。