Spring事務(wù)機(jī)制-傳播機(jī)制與隔離級別

一来累、Propagation (事務(wù)的傳播屬性)

Propagation-- key屬性確定代理應(yīng)該給哪個(gè)方法增加事務(wù)行為。這樣的屬性最重要的部份是傳播行為稻艰。有以下選項(xiàng)可供使用:

PROPAGATION_REQUIRED--支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就一個(gè)事務(wù)晶丘。是最常見的選擇。

PROPAGATION_SUPPORTS--支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù)浅浮,就以非事務(wù)方式執(zhí)行沫浆。

PROPAGATION_MANDATORY--支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù)滚秩,就拋出異常专执。

PROPAGATION_REQUIRES_NEW--新建事務(wù),如果當(dāng)前存在事務(wù)郁油,把當(dāng)前事務(wù)掛起本股,執(zhí)行當(dāng)前新建事務(wù)完成以后,上下文事務(wù)恢復(fù)再執(zhí)行桐腌。

PROPAGATION_NOT_SUPPORTED--以非事務(wù)方式執(zhí)行操作拄显,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起哩掺,執(zhí)行當(dāng)前邏輯凿叠,結(jié)束后恢復(fù)上下文的事務(wù)。

PROPAGATION_NEVER--以非事務(wù)方式執(zhí)行嚼吞,如果當(dāng)前存在事務(wù)盒件,則拋出異常。

1: PROPAGATION_REQUIRED

加入當(dāng)前正要執(zhí)行的事務(wù)不在另外一個(gè)事務(wù)里舱禽,那么就起一個(gè)新的事務(wù)

比如說炒刁,ServiceB.methodB的事務(wù)級別定義為PROPAGATION_REQUIRED, 那么由于執(zhí)行ServiceA.methodA的時(shí)候,

ServiceA.methodA已經(jīng)起了事務(wù)誊稚,這時(shí)調(diào)用ServiceB.methodB翔始,ServiceB.methodB看到自己已經(jīng)運(yùn)行在ServiceA.methodA

的事務(wù)內(nèi)部戳寸,就不再起新的事務(wù)退腥。而假如ServiceA.methodA運(yùn)行的時(shí)候發(fā)現(xiàn)自己沒有在事務(wù)中,他就會為自己分配一個(gè)事務(wù)甘晤。

這樣疾瓮,在ServiceA.methodA或者在ServiceB.methodB內(nèi)的任何地方出現(xiàn)異常脖镀,事務(wù)都會被回滾。即使ServiceB.methodB的事務(wù)已經(jīng)被

提交狼电,但是ServiceA.methodA在接下來fail要回滾蜒灰,ServiceB.methodB也要回滾

2: PROPAGATION_SUPPORTS

如果當(dāng)前在事務(wù)中,即以事務(wù)的形式運(yùn)行肩碟,如果當(dāng)前不再一個(gè)事務(wù)中强窖,那么就以非事務(wù)的形式運(yùn)行

3: PROPAGATION_MANDATORY

必須在一個(gè)事務(wù)中運(yùn)行。也就是說削祈,他只能被一個(gè)父事務(wù)調(diào)用翅溺。否則,他就要拋出異常

4: PROPAGATION_REQUIRES_NEW 執(zhí)行當(dāng)前新建事務(wù)完成以后,上下文事務(wù)恢復(fù)再執(zhí)行未巫。

這個(gè)就比較繞口了窿撬。 比如我們設(shè)計(jì)ServiceA.methodA的事務(wù)級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務(wù)級別為PROPAGATION_REQUIRES_NEW叙凡,

那么當(dāng)執(zhí)行到ServiceB.methodB的時(shí)候劈伴,ServiceA.methodA所在的事務(wù)就會掛起,ServiceB.methodB會起一個(gè)新的事務(wù)握爷,等待ServiceB.methodB的事務(wù)完成以后跛璧,

他才繼續(xù)執(zhí)行。他與PROPAGATION_REQUIRED 的事務(wù)區(qū)別在于事務(wù)的回滾程度了新啼。因?yàn)镾erviceB.methodB是新起一個(gè)事務(wù)追城,那么就是存在

兩個(gè)不同的事務(wù)。如果ServiceB.methodB已經(jīng)提交燥撞,那么ServiceA.methodA失敗回滾座柱,ServiceB.methodB是不會回滾的。如果ServiceB.methodB失敗回滾物舒,

如果他拋出的異常被ServiceA.methodA捕獲色洞,ServiceA.methodA事務(wù)仍然可能提交。

5: PROPAGATION_NOT_SUPPORTED

當(dāng)前不支持事務(wù)冠胯。比如ServiceA.methodA的事務(wù)級別是PROPAGATION_REQUIRED 火诸,而ServiceB.methodB的事務(wù)級別是PROPAGATION_NOT_SUPPORTED ,

那么當(dāng)執(zhí)行到ServiceB.methodB時(shí)荠察,ServiceA.methodA的事務(wù)掛起置蜀,而他以非事務(wù)的狀態(tài)運(yùn)行完,再繼續(xù)ServiceA.methodA的事務(wù)悉盆。

6: PROPAGATION_NEVER

不能在事務(wù)中運(yùn)行盯荤。假設(shè)ServiceA.methodA的事務(wù)級別是PROPAGATION_REQUIRED, 而ServiceB.methodB的事務(wù)級別是PROPAGATION_NEVER 焕盟,

那么ServiceB.methodB就要拋出異常了廷雅。

7: PROPAGATION_NESTED

理解Nested的關(guān)鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區(qū)別是京髓,PROPAGATION_REQUIRES_NEW另起一個(gè)事務(wù),將會與他的父事務(wù)相互獨(dú)立商架,

   而Nested的事務(wù)和他的父事務(wù)是相依的堰怨,他的提交是要等和他的父事務(wù)一塊提交的。也就是說蛇摸,如果父事務(wù)最后回滾备图,他也要回滾的。

而Nested事務(wù)的好處是他有一個(gè)savepoint。


ServiceA {

/**

  • 事務(wù)屬性配置為 PROPAGATION_REQUIRED

*/

void methodA() {

try {

//savepoint

ServiceB.methodB(); //PROPAGATION_NESTED 級別

} catch (SomeException) {

// 執(zhí)行其他業(yè)務(wù), 如 ServiceC.methodC();

}

}

}


    也就是說ServiceB.methodB失敗回滾揽涮,那么ServiceA.methodA也會回滾到savepoint點(diǎn)上抠藕,ServiceA.methodA可以選擇另外一個(gè)分支,比如ServiceC.methodC蒋困,繼續(xù)執(zhí)行盾似,來嘗試完成自己的事務(wù)。

   但是這個(gè)事務(wù)并沒有在EJB標(biāo)準(zhǔn)中定義雪标。

   那么什么是嵌套事務(wù)呢零院?很多人都不理解,我看過一些博客村刨,都是有些理解偏差告抄。

   嵌套是子事務(wù)套在父事務(wù)中執(zhí)行,子事務(wù)是父事務(wù)的一部分嵌牺,在進(jìn)入子事務(wù)之前打洼,父事務(wù)建立一個(gè)回滾點(diǎn),叫save point逆粹,然后執(zhí)行子事務(wù)募疮,這個(gè)子事務(wù)的執(zhí)行也算是父事務(wù)的一部分,然后子事務(wù)執(zhí)行結(jié)束枯饿,父事務(wù)繼續(xù)執(zhí)行酝锅。重點(diǎn)就在于那個(gè)save point∩莘剑看幾個(gè)問題就明了了:

1)如果子事務(wù)回滾搔扁,會發(fā)生什么?

父事務(wù)會回滾到進(jìn)入子事務(wù)前建立的save point蟋字,然后嘗試其他的事務(wù)或者其他的業(yè)務(wù)邏輯稿蹲,父事務(wù)之前的操作不會受到影響,更不會自動回滾鹊奖。

2)如果父事務(wù)回滾苛聘,會發(fā)生什么?

父事務(wù)回滾忠聚,子事務(wù)也會跟著回滾设哗!為什么呢,因?yàn)楦甘聞?wù)結(jié)束之前两蟀,子事務(wù)是不會提交的网梢,我們說子事務(wù)是父事務(wù)的一部分,正是這個(gè)道理赂毯。那么:

3)事務(wù)的提交战虏,是什么情況拣宰?

是父事務(wù)先提交,然后子事務(wù)提交烦感,還是子事務(wù)先提交巡社,父事務(wù)再提交?答案是第二種情況手趣,還是那句話晌该,子事務(wù)是父事務(wù)的一部分,由父事務(wù)統(tǒng)一提交回懦。

Spring事務(wù)的隔離級別

  1. ISOLATION_DEFAULT: 這是一個(gè)PlatfromTransactionManager默認(rèn)的隔離級別气笙,使用數(shù)據(jù)庫默認(rèn)的事務(wù)隔離級別.另外四個(gè)與JDBC的隔離級別相對應(yīng)

  2. ISOLATION_READ_UNCOMMITTED: 這是事務(wù)最低的隔離級別,它充許令外一個(gè)事務(wù)可以看到這個(gè)事務(wù)未提交的數(shù)據(jù)怯晕。這種隔離級別會產(chǎn)生臟讀潜圃,不可重復(fù)讀和幻像讀。

  3. ISOLATION_READ_COMMITTED: 保證一個(gè)事務(wù)修改的數(shù)據(jù)提交后才能被另外一個(gè)事務(wù)讀取舟茶。另外一個(gè)事務(wù)不能讀取該事務(wù)未提交的數(shù)據(jù)

  4. ISOLATION_REPEATABLE_READ: 這種事務(wù)隔離級別可以防止臟讀谭期,不可重復(fù)讀。但是可能出現(xiàn)幻像讀吧凉。它除了保證一個(gè)事務(wù)不能讀取另一個(gè)事務(wù)未提交的數(shù)據(jù)外隧出,還保證了避免下面的情況產(chǎn)生(不可重復(fù)讀)。

  5. ISOLATION_SERIALIZABLE 這是花費(fèi)最高代價(jià)但是最可靠的事務(wù)隔離級別阀捅。事務(wù)被處理為順序執(zhí)行胀瞪。除了防止臟讀,不可重復(fù)讀外饲鄙,還避免了幻像讀凄诞。

數(shù)據(jù)庫并發(fā)操作存在的異常情況:

  1. 更新丟失(Lost update): 兩個(gè)事務(wù)都同時(shí)更新一行數(shù)據(jù)但是第二個(gè)事務(wù)卻中途失敗退出導(dǎo)致對數(shù)據(jù)兩個(gè)修改都失效了這是系統(tǒng)沒有執(zhí) 行任何鎖操作因此并發(fā)事務(wù)并沒有被隔離開來。

  2. 臟讀热碳丁(Dirty Reads): 一個(gè)事務(wù)開始讀取 了某行數(shù)據(jù)但是另外一個(gè)事務(wù)已經(jīng)更新了此數(shù)據(jù)但沒有能夠及時(shí)提交帆谍。這是相當(dāng)危險(xiǎn)很可能所有操作都被回滾。

  3. 不可重復(fù)讀戎嵩邸(Non-repeatable Reads): 一 個(gè)事務(wù)對同一行數(shù)據(jù)重復(fù)讀取兩次但是卻得到了不同結(jié)果汛蝙。例如在兩次讀取中途有另外一個(gè)事務(wù)對該行數(shù)據(jù)進(jìn)行了修改并提交。

  4. 兩次更新問題(Second lost updates problem): 無法重復(fù)讀取特例朴肺,有兩個(gè)并發(fā)事務(wù)同時(shí)讀取同一行數(shù)據(jù)然后其中一個(gè)對它進(jìn)行修改提交而另一個(gè)也進(jìn)行了修改提交這就會造成 第一次寫操作失效窖剑。

  5. 幻讀(Phantom Reads): 也稱為幻像(幻影)。事務(wù)在操作過程中進(jìn)行兩次查詢戈稿,第二次查詢結(jié)果包含了第一次查詢中未出現(xiàn)的數(shù)據(jù)(這里并不要求兩次查詢SQL語句相同)這是因?yàn)樵趦纱尾樵冞^程中有 另外一個(gè)事務(wù)插入數(shù)據(jù)造成的苛吱。

    為了避免上面出現(xiàn)幾種情況在標(biāo)準(zhǔn)SQL規(guī)范中定義了4個(gè)事務(wù)隔離級別,不同隔離級別對事務(wù)處理不同 器瘪。

1.未授權(quán)讀却浯ⅰ(Read Uncommitted): 也稱 未提交讀。允許臟讀取但不允許更新丟失橡疼,如果一個(gè)事務(wù)已經(jīng)開始寫數(shù)據(jù)則另外一個(gè)數(shù)據(jù)則不允許同時(shí)進(jìn)行寫操作但允許其他事務(wù)讀此行數(shù)據(jù)援所。該隔離級別可以通過 “排他寫鎖”實(shí)現(xiàn)。事務(wù)隔離的最低級別欣除,僅可保證不讀取物理損壞的數(shù)據(jù)住拭。與READ COMMITTED 隔離級相反,它允許讀取已經(jīng)被其它用戶修改但尚未提交確定的數(shù)據(jù)历帚。

  1. 授權(quán)讀忍显馈(Read Committed): 也稱提交 讀。允許不可重復(fù)讀取但不允許臟讀取挽牢。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實(shí)現(xiàn)谱煤,讀取數(shù)據(jù)的事務(wù)允許其他事務(wù)繼續(xù)訪問該行數(shù)據(jù),但是未提交寫事務(wù)將 會禁止其他事務(wù)訪問該行禽拔。SQL Server 默認(rèn)的級別刘离。在此隔離級下,SELECT 命令不會返回尚未提交(Committed) 的數(shù)據(jù)睹栖,也不能返回臟數(shù)據(jù)硫惕。

  2. 可重復(fù)讀取(Repeatable Read): 禁止 不可重復(fù)讀取和臟讀取野来。但是有時(shí)可能出現(xiàn)幻影數(shù)據(jù)恼除,這可以通過“共享讀鎖”和“排他寫鎖”實(shí)現(xiàn),讀取數(shù)據(jù)事務(wù)將會禁止寫事務(wù)(但允許讀事務(wù))曼氛,寫事務(wù)則禁 止任何其他事務(wù)豁辉。在此隔離級下,用SELECT 命令讀取的數(shù)據(jù)在整個(gè)命令執(zhí)行過程中不會被更改搪锣。此選項(xiàng)會影響系統(tǒng)的效能秋忙,非必要情況最好不用此隔離級。

  3. 串行(Serializable): 也稱可串行讀构舟。提 供嚴(yán)格的事務(wù)隔離灰追,它要求事務(wù)序列化執(zhí)行,事務(wù)只能一個(gè)接著一個(gè)地執(zhí)行狗超,但不能并發(fā)執(zhí)行弹澎。如果僅僅通過“行級鎖”是無法實(shí)現(xiàn)事務(wù)序列化的,必須通過其他機(jī) 制保證新插入的數(shù)據(jù)不會被剛執(zhí)行查詢操作事務(wù)訪問到努咐。事務(wù)隔離的最高級別苦蒿,事務(wù)之間完全隔離。如果事務(wù)在可串行讀隔離級別上運(yùn)行渗稍,則可以保證任何并發(fā)重疊 事務(wù)均是串行的佩迟。

隔離級別 更新丟失 臟讀取 重復(fù)讀取 幻讀

未授權(quán)讀取 N Y Y Y

授權(quán)讀取 N N Y Y

可重復(fù) 讀取 N N N Y

串行 N N N N

所以最安全的团滥,是Serializable,但是伴隨而來也是高昂的性能開銷报强。

另外灸姊,事務(wù)常用的兩個(gè)屬性:readonly和timeout

一個(gè)是設(shè)置事務(wù)為只讀以提升性能。

另一個(gè)是設(shè)置事務(wù)的超時(shí)時(shí)間秉溉,一般用于防止大事務(wù)的發(fā)生力惯。還是那句話,事務(wù)要盡可能的姓偎弧父晶!

最后引入一個(gè)問題:

一個(gè)邏輯操作需要檢查的條件有20條,能否為了減小事務(wù)而將檢查性的內(nèi)容放到事務(wù)之外呢弄跌?

很多系統(tǒng)都是在DAO的內(nèi)部開始啟動事務(wù)甲喝,然后進(jìn)行操作,最后提交或者回滾碟绑。這其中涉及到代碼設(shè)計(jì)的問題俺猿。小一些的系統(tǒng)可以采用這種方式來做,但是在一些比較大的系統(tǒng)格仲,

邏輯較為復(fù)雜的系統(tǒng)中押袍,勢必會將過多的業(yè)務(wù)邏輯嵌入到DAO中,導(dǎo)致DAO的復(fù)用性下降凯肋。所以這不是一個(gè)好的實(shí)踐谊惭。

來回答這個(gè)問題:能否為了縮小事務(wù),而將一些業(yè)務(wù)邏輯檢查放到事務(wù)外面侮东?答案是:對于核心的業(yè)務(wù)檢查邏輯圈盔,不能放到事務(wù)之外,而且必須要作為分布式下的并發(fā)控制悄雅!

一旦在事務(wù)之外做檢查驱敲,那么勢必會造成事務(wù)A已經(jīng)檢查過的數(shù)據(jù)被事務(wù)B所修改,導(dǎo)致事務(wù)A徒勞無功而且出現(xiàn)并發(fā)問題宽闲,直接導(dǎo)致業(yè)務(wù)控制失敗众眨。

所以,在分布式的高并發(fā)環(huán)境下容诬,對于核心業(yè)務(wù)邏輯的檢查娩梨,要采用加鎖機(jī)制。

比如事務(wù)開啟需要讀取一條數(shù)據(jù)進(jìn)行驗(yàn)證览徒,然后邏輯操作中需要對這條數(shù)據(jù)進(jìn)行修改狈定,最后提交。

這樣的一個(gè)過程习蓬,如果讀取并驗(yàn)證的代碼放到事務(wù)之外纽什,那么讀取的數(shù)據(jù)極有可能已經(jīng)被其他的事務(wù)修改措嵌,當(dāng)前事務(wù)一旦提交,又會重新覆蓋掉其他事務(wù)的數(shù)據(jù)芦缰,導(dǎo)致數(shù)據(jù)異常铅匹。

所以在進(jìn)入當(dāng)前事務(wù)的時(shí)候,必須要將這條數(shù)據(jù)鎖住饺藤,使用for update就是一個(gè)很好的在分布式環(huán)境下的控制手段。

一種好的實(shí)踐方式是使用編程式事務(wù)而非生命式流礁,尤其是在較為規(guī)模的項(xiàng)目中涕俗。對于事務(wù)的配置,在代碼量非常大的情況下神帅,將是一種折磨再姑,而且人肉的方式,絕對不能避免這種問題找御。

將DAO保持針對一張表的最基本操作元镀,然后業(yè)務(wù)邏輯的處理放入manager和service中進(jìn)行,同時(shí)使用編程式事務(wù)更精確的控制事務(wù)范圍霎桅。

特別注意的栖疑,對于事務(wù)內(nèi)部一些可能拋出異常的情況,捕獲要謹(jǐn)慎滔驶,不能隨便的catch Exception 導(dǎo)致事務(wù)的異常被吃掉而不能正秤龈铮回滾。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末揭糕,一起剝皮案震驚了整個(gè)濱河市萝快,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌著角,老刑警劉巖揪漩,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吏口,居然都是意外死亡奄容,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門锨侯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嫩海,“玉大人,你說我怎么就攤上這事囚痴∪郑” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵深滚,是天一觀的道長奕谭。 經(jīng)常有香客問我涣觉,道長,這世上最難降的妖魔是什么血柳? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任官册,我火速辦了婚禮,結(jié)果婚禮上难捌,老公的妹妹穿的比我還像新娘膝宁。我一直安慰自己,他們只是感情好根吁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布员淫。 她就那樣靜靜地躺著,像睡著了一般击敌。 火紅的嫁衣襯著肌膚如雪介返。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天沃斤,我揣著相機(jī)與錄音圣蝎,去河邊找鬼。 笑死衡瓶,一個(gè)胖子當(dāng)著我的面吹牛徘公,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鞍陨,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼步淹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了诚撵?” 一聲冷哼從身側(cè)響起缭裆,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寿烟,沒想到半個(gè)月后澈驼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筛武,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年缝其,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片徘六。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡内边,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出待锈,到底是詐尸還是另有隱情漠其,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站和屎,受9級特大地震影響拴驮,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柴信,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一套啤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧随常,春花似錦潜沦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至钞楼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間袄琳,已是汗流浹背询件。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唆樊,地道東北人宛琅。 一個(gè)月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像逗旁,于是被迫代替她去往敵國和親嘿辟。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容