數(shù)據(jù)庫事務(wù)(Database Transaction) ,是指作為單個(gè)邏輯工作單元執(zhí)行的一系列操作,要么完全地執(zhí)行,要么完全地不執(zhí)行。 滿足ACID(原子性,一致性,隔離性,持久性)。
原子性:要么全部執(zhí)行,要么全部不執(zhí)行。原子性消除了操作子集的可能性,如購物系統(tǒng)中只執(zhí)行了付款生成訂單的操作,卻沒有執(zhí)行庫存-1的操作。
一致性:事務(wù)在完成時(shí),必須使所有的數(shù)據(jù)都保持一致狀態(tài)。舉例,在兩個(gè)賬戶中總共存有5000塊錢,無論兩個(gè)賬戶之間怎么相互轉(zhuǎn)賬,兩個(gè)賬戶總額一定還是5000
隔離性:由并發(fā)事務(wù)所作的修改必須與任何其它并發(fā)事務(wù)所作的修改隔離。事務(wù)查看數(shù)據(jù)時(shí)數(shù)據(jù)所處的狀態(tài),要么是另一并發(fā)事務(wù)修改它之前的狀態(tài),要么是另一事務(wù)修改它之后的狀態(tài),事務(wù)不會(huì)查看中間狀態(tài)的數(shù)據(jù)。如T2要么在T1開始之前就已經(jīng)結(jié)束,要么在T1結(jié)束之后才開始,這樣每個(gè)事務(wù)都感覺不到有其他事務(wù)在并發(fā)地執(zhí)行。這稱為隔離性,因?yàn)樗軌蛑匦卵b載起始數(shù)據(jù),并且重播一系列事務(wù),以使數(shù)據(jù)結(jié)束時(shí)的狀態(tài)與原始事務(wù)執(zhí)行的狀態(tài)相同。當(dāng)事務(wù)可序列化時(shí)將獲得最高的隔離級別。在此級別上,從一組可并行執(zhí)行的事務(wù)獲得的結(jié)果與通過連續(xù)運(yùn)行每個(gè)事務(wù)所獲得的結(jié)果相同。由于高度隔離會(huì)限制可并行執(zhí)行的事務(wù)數(shù),所以一些應(yīng)用程序降低隔離級別以換取更大的吞吐量。
持久性:事務(wù)完成之后,它對于系統(tǒng)的影響是永久性的。該修改即使出現(xiàn)致命的系統(tǒng)故障也將一直保持。
單個(gè)controller中有兩個(gè)service,第一個(gè)service成功執(zhí)行,第二個(gè)service失敗則不會(huì)讓第一個(gè)回滾
解決方案:將兩個(gè)service服務(wù)放入一個(gè)service當(dāng)中。使用相同的DAO。
單個(gè)controller中,在一個(gè)service中執(zhí)行數(shù)據(jù)庫操作并調(diào)用另一個(gè)service執(zhí)行數(shù)據(jù)庫操作,拋出異常過后發(fā)現(xiàn)事務(wù)一起回滾了(事務(wù)的傳播屬性行為PROPAGATION_REQUIRED ,service2在執(zhí)行的時(shí)候發(fā)現(xiàn)service1已經(jīng)開啟了事務(wù),這時(shí)候便直接加入第一個(gè)事務(wù),如果第一個(gè)沒有開啟一個(gè)事務(wù),則service2自己開啟一個(gè)事務(wù),這樣不管是在第一個(gè)還是第二個(gè)service中出現(xiàn)異常都能回滾)。
事務(wù)的傳播屬性參考:https://blog.csdn.net/mawenshu316143866/article/details/81281443
? ? 1.PROPAGATION_REQUIRED -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就新建一個(gè)事務(wù)。這是最常見的選擇(也是spring默認(rèn)的)。
2.PROPAGATION_SUPPORTS -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。
假設(shè)ServiceB.methodB() 的事務(wù)級別為 PROPAGATION_SUPPORTS,那么當(dāng)執(zhí)行到ServiceB.methodB()時(shí),如果發(fā)現(xiàn)ServiceA.methodA()已經(jīng)開啟了一個(gè)事務(wù),則加入當(dāng)前的事務(wù),如果發(fā)現(xiàn)ServiceA.methodA()沒有開啟事務(wù),則自己也不開啟事務(wù)。這種時(shí)候,內(nèi)部方法的事務(wù)性完全依賴于最外層的事務(wù)。
3.PROPAGATION_MANDATORY -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。
MANDATORY 行為下當(dāng)前連接不具備事務(wù),會(huì)拋出異常,這種行為一般很少使用。
4.PROPAGATION_REQUIRES_NEW -- 新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。
比如我們設(shè)計(jì) ServiceA.methodA() 的事務(wù)級別為 PROPAGATION_REQUIRED,ServiceB.methodB() 的事務(wù)級別為 PROPAGATION_REQUIRES_NEW。那么當(dāng)執(zhí)行到 ServiceB.methodB() 的時(shí)候,ServiceA.methodA() 所在的事務(wù)就會(huì)掛起,ServiceB.methodB() 會(huì)起一個(gè)新的事務(wù),等待 ServiceB.methodB() 的事務(wù)完成以后,它才繼續(xù)執(zhí)行。他與 PROPAGATION_REQUIRED 的事務(wù)區(qū)別在于事務(wù)的回滾程度了。因?yàn)?ServiceB.methodB() 是新起一個(gè)事務(wù),那么就是存在兩個(gè)不同的事務(wù)。如果 ServiceB.methodB() 已經(jīng)提交,那么 ServiceA.methodA() 失敗回滾,ServiceB.methodB() 是不會(huì)回滾的。如果 ServiceB.methodB() 失敗回滾,如果他拋出的異常被 ServiceA.methodA() 捕獲,ServiceA.methodA() 事務(wù)仍然可能提交(主要看B拋出的異常是不是A會(huì)回滾的異常)
5.PROPAGATION_NOT_SUPPORTED -- 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
事務(wù)2在?NOT_SUPPORTED?行為下,會(huì)掛起當(dāng)前事務(wù)。與?REQUIRES_NEW 行為不同的是,它在掛起之后不會(huì)在嘗試開啟新的事務(wù)。也就是這一點(diǎn)的區(qū)別決定了?NOT_SUPPORTED 行為與?REQUIRES_NEW 行為。比如用于事務(wù)操作之間的日志記錄等
6.PROPAGATION_NEVER -- 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
MANDATORY 行為下當(dāng)前連接具備事務(wù),會(huì)拋出異常,這種行為一般很少使用。
7.PROPAGATION_NESTED -- 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),
事務(wù)的隔離級別:
臟讀:一個(gè)事務(wù)讀取到了另外一個(gè)事務(wù)沒有提交的數(shù)據(jù)。讀“臟”數(shù)據(jù)是指事務(wù)T1修改某一數(shù)據(jù),并將其寫回磁盤,事務(wù)T2讀取同一數(shù)據(jù)后,T1由于某種原因被除撤消,而此時(shí)T1把已修改過的數(shù)據(jù)又恢復(fù)原值,T2讀到的數(shù)據(jù)與數(shù)據(jù)庫的數(shù)據(jù)不一致,則T2讀到的數(shù)據(jù)就為“臟”數(shù)據(jù),即不正確的數(shù)據(jù)。例如:一個(gè)編輯人員正在更改電子文檔。在更改過程中,另一個(gè)編輯人員復(fù)制了該文檔(該復(fù)本包含到目前為止所做的全部更改)并將其分發(fā)給預(yù)期的用戶。此后,第一個(gè)編輯人員認(rèn)為所做的更改是錯(cuò)誤的,于是刪除了所做的編輯并保存了文檔。分發(fā)給用戶的文檔包含不再存在的編輯內(nèi)容,并且這些編輯內(nèi)容應(yīng)認(rèn)為從未存在過。如果在第一個(gè)編輯人員確定最終更改前任何人都不能讀取更改的文檔,則可以避免該問題。
臟讀的解決方案:解決臟讀問題:修改時(shí)加排他鎖,直到事務(wù)提交后才釋放,讀取時(shí)加共享鎖,讀取完釋放事務(wù)1讀取數(shù)據(jù)時(shí)加上共享鎖后(這樣在事務(wù)1讀取數(shù)據(jù)的過程中,其他事務(wù)就不會(huì)修改該數(shù)據(jù)),不允許任何事務(wù)操作該數(shù)據(jù),只能讀取,之后1如果有更新操作,那么會(huì)轉(zhuǎn)換為排他鎖,其他事務(wù)更無權(quán)參與進(jìn)來讀寫,這樣就防止了臟讀問題。但是當(dāng)事務(wù)1讀取數(shù)據(jù)過程中,有可能其他事務(wù)也讀取了該數(shù)據(jù),讀取完畢后共享鎖釋放,此時(shí)事務(wù)1修改數(shù)據(jù),修改完畢提交事務(wù),其他事務(wù)再次讀取數(shù)據(jù)時(shí)候發(fā)現(xiàn)數(shù)據(jù)不一致,就會(huì)出現(xiàn)不可重復(fù)讀問題,所以這樣不能夠避免不可重復(fù)讀問題。
幻讀:幻讀是指當(dāng)事務(wù)不是獨(dú)立執(zhí)行時(shí)發(fā)生的一種現(xiàn)象,例如第一個(gè)事務(wù)對一個(gè)表中的數(shù)據(jù)進(jìn)行了修改,這種修改涉及到表中的全部數(shù)據(jù)行。同時(shí),第二個(gè)事務(wù)也修改這個(gè)表中的數(shù)據(jù),這種修改是向表中插入一行新數(shù)據(jù)。那么,以后就會(huì)發(fā)生操作第一個(gè)事務(wù)的用戶發(fā)現(xiàn)表中還有沒有修改的數(shù)據(jù)行,就好象發(fā)生了幻覺一樣。按一定條件從數(shù)據(jù)庫中讀取了某些記錄后,T2刪除了其中部分記錄,當(dāng)T1再次按相同條件讀取數(shù)據(jù)時(shí),發(fā)現(xiàn)某些記錄消失T1按一定條件從數(shù)據(jù)庫中刪除某些數(shù)據(jù)記錄后,T2插入了一些記錄,當(dāng)T1再次按相同條件讀取數(shù)據(jù)時(shí),發(fā)現(xiàn)多了一些記錄。
不可重復(fù)讀:指事務(wù)T1讀取數(shù)據(jù)后,事務(wù)T2執(zhí)行更新操作,使T1無法讀取前一次結(jié)果。不可重復(fù)讀包括三種情況:事務(wù)T1讀取某一數(shù)據(jù)后,T2對其做了修改,當(dāng)T1再次讀該數(shù)據(jù)后,得到與前一不同的值。
更新丟失:當(dāng)兩個(gè)或多個(gè)事務(wù)選擇同一行,然后基于最初選定的值更新該行時(shí),會(huì)發(fā)生丟失更新問題。每個(gè)事務(wù)都不知道其它事務(wù)的存在。最后的更新將重寫由其它事務(wù)所做的更新,這將導(dǎo)致數(shù)據(jù)丟失。如上例。再例如,兩個(gè)編輯人員制作了同一文檔的電子復(fù)本。每個(gè)編輯人員獨(dú)立地更改其復(fù)本,然后保存更改后的復(fù)本,這樣就覆蓋了原始文檔。最后保存其更改復(fù)本的編輯人員覆蓋了第一個(gè)編輯人員所做的更改。如果在第一個(gè)編輯人員完成之后第二個(gè)編輯人員才能進(jìn)行更改,則可以避免該問題。
共享鎖:(讀鎖)共享鎖又稱讀鎖,是讀取操作創(chuàng)建的鎖。其他用戶可以并發(fā)讀取數(shù)據(jù),但任何事務(wù)都不能對數(shù)據(jù)進(jìn)行修改(獲取數(shù)據(jù)上的排他鎖),直到已釋放所有共享鎖。
如果事務(wù)T對數(shù)據(jù)A加上共享鎖后,則其他事務(wù)只能對A再加共享鎖,不能加排他鎖。獲準(zhǔn)共享鎖的事務(wù)只能讀數(shù)據(jù),不能修改數(shù)據(jù)。
排他鎖:(寫鎖)排他鎖又稱寫鎖,如果事務(wù)T對數(shù)據(jù)A加上排他鎖后,則其他事務(wù)不能再對A加任任何類型的封鎖。獲準(zhǔn)排他鎖的事務(wù)既能讀數(shù)據(jù),又能修改數(shù)據(jù)。
隔離級別:
????① Serializable (串行化):可避免臟讀、不可重復(fù)讀、幻讀的發(fā)生。
?、?Repeatable read (可重復(fù)讀):可避免臟讀、不可重復(fù)讀的發(fā)生。(mysql默認(rèn)級別)
?、?Read committed (讀已提交):可避免臟讀的發(fā)生。
?、?Read uncommitted (讀未提交):最低級別,任何情況都無法保證。