一、事務傳播性
1.1 什么是事務的傳播性
事務的傳播性一般在事務嵌套時候使用,比如在事務A里面調用了另外一個使用事務的方法,那么這倆個事務是各自作為獨立的事務執行提交,還是內層的事務合并到外層的事務一塊提交那,這就是事務傳播性要確定的問題。下面一一介紹比較常用的事務傳播性。
1.2 PROPAGATION_REQUIRED
Spring默認的事務傳播機制,如果外層有事務則當前事務加入到外層事務,一塊提交一塊回滾,如果外層沒有事務則當前開啟一個新事務。這個機制可以滿足大多數業務場景。
試驗:
public class BoA { public void test(){ boB.sayHello(); }}
BoA 和boB都是進行過事務增強后的bo,那么在執行test的時候會開啟一個事務(或者test調用方已經存在事務則加入該事務),執行到sayHello()時候由于傳播性是PROPAGATION_REQUIRED,所以sayHello方法加入到test的事務,那么sayHello和test就會同時提交,同時回滾。值得注意的是如果test里面調用sayHello時候加了trycatch沒有把異常跑出去,而sayHello方法卻拋出了異常,那么整個事務也會回滾,這時候調用test的外層會受到"Transaction rolled back because it has been marked as rollback-only的異常,而把sayHello真正的異常吃掉了。
平時我們都是在bo里面調用數據庫操作,在rpc和screen調用bo,所以bo層不應該catch掉異常,而應該拋出來,在rpc和screen層catch異常。
1.3 PROPAGATION_REQUIRES_NEW
該傳播機制是每次新開啟一個事務,同時把外層的事務掛起,當前新事務執行完畢后在恢復上層事務的執行。
以上面代碼為例,首先進入test方法前會開啟一個事務,然后調用sayHello時候會把test的事務掛起,從新開啟一個新事務執行sayHello,執行完畢后恢復test的事務。如果sayHello拋出來異常則sayHello的事務會回滾,那么test方法是否回滾那?這個要看情況,如果test在調用sayHello 時候使用了trycatch并且異常沒有在catch中throw出來,那么test方法不會回滾,這時候sayHello是提交和回滾對test沒有影響,。
如果test中沒有加trycatch那么,test也會回滾。
1.4 PROPAGATION_SUPPORTS
該傳播機制如果外層有事務則加入該事務,如果不存在也不會創建新事務,直接使用非事務方式執行。
以上面代碼為例,由于PROPAGATION_SUPPORTS,所以test和sayHello都沒有開啟事務,沒啥好講的。
下面看下如果test隔離級別是PROPAGATION_REQUIRED,sayHello傳播性是PROPAGATION_SUPPORTS的情況。這時候外層test會開啟一個事務(或者test調用方已經存在事務則加入該事務),然后sayHello執行時候會加入到test的事務和1.2類似,同時提交同時回滾。
1.5 PROPAGATION_NOT_SUPPORTED
該傳播機制不支持事務,如果外層存在事務則掛起外層事務 ,然后執行當前邏輯,執行完畢后,恢復外層事務。
同樣這里看下如果test使用PROPAGATION_REQUIRED,sayHello傳播性是PROPAGATION_NOT_SUPPORTED的情況,首先test會開啟一個事務(或者test調用方已經存在事務則加入該事務),然后sayHello執行時候會掛起該事務然后在非事務內做自己的事情,做完后在恢復test的事務。 無論sayHello是否拋出異常,sayHello的事務都不會回滾,因為它不在事務范圍內,那test?
這個就和1.3一樣了,如果test catch住sayHello的異常沒有throw出去,那么test就不回滾,否者回滾。
1.6 PROPAGATION_NEVER
該傳播機制不支持事務,如果外層存在事務則直接拋出異常。
IllegalTransactionStateException( "Existing transaction found for transaction marked with propagation 'never'")
1.7 PROPAGATION_MANDATORY
該傳播機制是說配置了該傳播性的方法只能在已經存在事務的方法中被調用,如果在不存在事務的方法中被調用,會拋出異常。
IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'");
1.8 PROPAGATION_NESTED
該傳播機制特點是可以保存狀態保存點,當事務回滾后會回滾到某一個保存點上,從而避免所有嵌套事務都回滾。
上面代碼test和sayHello都設置為PROPAGATION_NESTED,如果sayHello拋出異常,兩者還是都回滾了,因為sayHello雖然回滾到了savePoint而savepoint里面確實包含了test的操作,但是savepoint后還是會吧異常throw給test,這導致了test的回滾。
總結,只有傳播性為PROPAGATION_REQUIRED||PROPAGATION_REQUIRES_NEW||PROPAGATION_NESTED時候才可能開啟一個新事務。
二、事務隔離性
2.1 什么是事務的隔離性
事務的隔離性是指多個事務并發執行的時候相互之間不受到彼此的干擾,是事務acid中i,根據隔離程度對隔離性有會分類。在具體介紹事務隔離性前有必要介紹幾個名詞說明數據庫并發操作存在的問題。
2.1.1 臟讀
所謂臟讀是指一個事務中訪問到了另外一個事務未提交的數據,具體來說假如有兩個事務A和B同時更新一個數據d=1,事務B先執行了select獲取到d=1,然后更新d=2但是沒有提交,這時候事務A在B沒有提交的情況下執行搜索結果d=2,這就是臟讀。
2.1.2 不可重復讀
所謂不可重復讀是指一個事務內在未提交的前提下多次搜索一個數據,搜出來的結果不一致。發生不可重復讀的原因是在多次搜索期間這個數據被其他事務更新了。
2.1.3 幻讀
所謂幻讀是指同一個事務內多次查詢(注意查詢的sql不一定一樣)返回的結果集的不一樣(比如新增或者少了一條數據),比如同一個事務A內第一次查詢時候有n條記錄,但是第二次同等條件下查詢卻又n+1條記錄,這就好像產生了幻覺,為啥兩次結果不一樣那。其實和不可重復讀一樣,發生幻讀的原因也是另外一個事務新增或者刪除或者修改了第一個事務結果集里面的數據。不同在于不可重復讀是數據內容被修改了,幻讀是數據變多了或者少了。
2.2、事務隔離級別
為了解決事務并發帶來的問題,才有了sql規范中的四個事務隔離級別,不同隔離級別對上面三個問題部分或者全部做了避免。注意:下面試驗用的兩個終端都是同時執行了begin為了模擬事務并發。
2.2.1 Read Uncommitted
讀未提交隔離級別,就是指一個事務中可以讀取其他事務未提交的數據,這個級別會導致臟讀。
本文都是以mysql為例引擎InnoDB,mysql默認事務隔離級別為Repeatable_Read:如圖:

下面我們更改隔離級別為Read Uncommitted :

試驗:
下面我們打開兩個mysql終端,并且關閉自動提交.
終端一:

終端二:

終端一我們開啟了一個事務,并且插入了一條數據但是沒有提交事務,但是終端二卻查詢出來了。
終端一執行rollback:

終端二搜索:

在終端1回滾后,終端二有搜不到了,所以有可能在終端一沒有回滾時候終端二已經獲取并使用終端一的數據,而終端一回滾后,數據已經被使用過了,所以導致了臟讀。
總結:該隔離級別會導致 臟讀,不可重復讀,幻讀,是最低級的隔離級別,一般不用的。
2.2.2 Read Committed
讀已提交隔離級別,一個事務只能讀取到其他事務已經提交的數據,可能導致同一個事務中多次搜查結果不一樣。
試驗:修改事務隔離級別為Read Committed,

終端一:

終端二:

由于終端一執行后沒有commit,所以終端二查詢不到。
下面終端一執行commit:

終端二再次執行查詢:

終端一提交后,終端二就可以搜查出來了。
總結:該隔離級別會導致不可重復讀和幻讀,避免了臟讀,oracle默認是該隔離級別。實際項目使用mybaits時候雖然隔離級別是read committed,但是在一個事務中多次搜索還是會是同一個結果,這是因為mybatis一級緩存的原因
2.2.3 Repeatable Read
可重復讀隔離級別,一個事務內多次查詢數據時候查詢的數據內容和第一次查詢的一致也就是說第一次查詢出來的數據沒有被修改,而不管其他事務有沒有對這些數據新修改。但是可能其他事務新增一條數據,導致一個事務內查詢的結果集里面多了一條記錄。mysql默認隔離級別就是這個。
試驗:
首先修改事務隔離級別為可重復讀:

模擬修改數據情況:
終端一:

終端二:

可以知道終端一已經提交的數據在終端二的事務中還是查不到(注意終端二執行begin要在終端一執行commit前,因為我們要模擬并發事務)。
下面在模擬下新增數據情況
終端一:

終端二:

終端一插入了一條記錄并且提交,但是終端二還是查詢不到新增的記錄
總結:這里有點奇怪,按照其他資料顯示該隔離級別應該是避免了 臟讀,不可重復讀,但是還存在幻讀,但是試驗表明不存在幻讀
2.2.4 Serializable
串行化隔離級別,就是多個事務串行化一個個按照順序執行,這種不存在并發情況,所以可以避免所有事務并發問題。
終端一:

終端二:

可以看到終端一打開一個事務后,事務二的insert語句會等待知道事務一提交或者超時。
超時:

鏈接:https://www.jianshu.com/p/249f2cd42692