javascript
Spring五个事务隔离级别和七个事务传播行为
Spring五個事務隔離級別和七個事務傳播行為
1.?臟讀?:臟讀就是指當一個事務正在訪問數(shù)據(jù),并且對數(shù)據(jù)進行了修改,而這種修改還沒有提交到數(shù)據(jù)庫中,這時,另外一個事務也訪問這個數(shù)據(jù),然后使用了這個數(shù)據(jù)。
2.?不可重復讀?:是指在一個事務內(nèi),多次讀同一數(shù)據(jù)。在這個事務還沒有結(jié)束時,另外一個事務也訪問該同一數(shù)據(jù)。那么,在第一個事務中的兩 次讀數(shù)據(jù)之間,由于第二個事務的修改,那么第一個事務兩次讀到的的數(shù)據(jù)可能是不一樣的。這樣就發(fā)生了在一個事務內(nèi)兩次讀到的數(shù)據(jù)是不一樣的,因此稱為是不 可重復讀。例如,一個編輯人員兩次讀取同一文檔,但在兩次讀取之間,作者重寫了該文檔。當編輯人員第二次讀取文檔時,文檔已更改。原始讀取不可重復。如果 只有在作者全部完成編寫后編輯人員才可以讀取文檔,則可以避免該問題。?…數(shù)據(jù)庫事務和Spring事務是一般面試都會被提到,很多朋友寫慣了代碼,很少花時間去整理歸納這些東西,結(jié)果本來會的東西,居然吞吞吐吐答不上來。
下面是我收集到一些關(guān)于Spring事務的問題,希望能幫助大家過關(guān)。
3.?幻讀?: 是指當事務不是獨立執(zhí)行時發(fā)生的一種現(xiàn)象,例如第一個事務對一個表中的數(shù)據(jù)進行了修改,這種修改涉及到表中的全部數(shù)據(jù)行。 同時,第二個事務也修改這個表中的數(shù)據(jù),這種修改是向表中插入一行新數(shù)據(jù)。那么,以后就會發(fā)生操作第一個事務的用戶發(fā)現(xiàn)表中還有沒有修改的數(shù)據(jù)行,就好象 發(fā)生了幻覺一樣。例如,一個編輯人員更改作者提交的文檔,但當生產(chǎn)部門將其更改內(nèi)容合并到該文檔的主復本時,發(fā)現(xiàn)作者已將未編輯的新材料添加到該文檔中。 如果在編輯人員和生產(chǎn)部門完成對原始文檔的處理之前,任何人都不能將新材料添加到文檔中,則可以避免該問題。
補充 :?基于元數(shù)據(jù)的 Spring 聲明性事務 :
Isolation 屬性一共支持五種事務設(shè)置,具體介紹如下:
<!—->l????????? <!—->DEFAULT 使用數(shù)據(jù)庫設(shè)置的隔離級別 ( 默認 ) ,由 DBA 默認的設(shè)置來決定隔離級別 .
<!—->l????????? <!—->READ_UNCOMMITTED 會出現(xiàn)臟讀、不可重復讀、幻讀 ( 隔離級別最低,并發(fā)性能高 )
<!—->l????????? <!—->READ_COMMITTED? 會出現(xiàn)不可重復讀、幻讀問題(鎖定正在讀取的行)
<!—->l????????? <!—->REPEATABLE_READ 會出幻讀(鎖定所讀取的所有行)
<!—->l????????? <!—->SERIALIZABLE 保證所有的情況不會發(fā)生(鎖表)
不可重復讀的重點是修改?:?
同樣的條件 , ? 你讀取過的數(shù)據(jù) , ? 再次讀取出來發(fā)現(xiàn)值不一樣了?
幻讀的重點在于新增或者刪除?
同樣的條件 , ? 第 1 次和第 2 次讀出來的記錄數(shù)不一樣
Spring在TransactionDefinition接口中定義這些屬性
在TransactionDefinition接口中定義了五個不同的事務隔離級別
ISOLATION_DEFAULT?這是一個PlatfromTransactionManager默認的隔離級別,使用數(shù)據(jù)庫默認的事務隔離級別.另外四個與JDBC的隔離級別相對應?
ISOLATION_READ_UNCOMMITTED?這是事務最低的隔離級別,它充許別外一個事務可以看到這個事務未提交的數(shù)據(jù)。這種隔離級別會產(chǎn)生臟讀,不可重復讀和幻像讀
ISOLATION_READ_COMMITTED?保證一個事務修改的數(shù)據(jù)提交后才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數(shù)據(jù)。這種事務隔離級別可以避免臟讀出現(xiàn),但是可能會出現(xiàn)不可重復讀和幻像讀。
ISOLATION_REPEATABLE_READ?這種事務隔離級別可以防止臟讀,不可重復讀。但是可能出現(xiàn)幻像讀。它除了保證一個事務不能讀取另一個事務未提交的數(shù)據(jù)外,還保證了避免下面的情況產(chǎn)生(不可重復讀)。
ISOLATION_SERIALIZABLE?這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執(zhí)行。除了防止臟讀,不可重復讀外,還避免了幻像讀。
?
在TransactionDefinition接口中定義了七個事務傳播行為。
PROPAGATION_REQUIRED?如果存在一個事務,則支持當前事務。如果沒有事務則開啟一個新的事務。
PROPAGATION_SUPPORTS?如果存在一個事務,支持當前事務。如果沒有事務,則非事務的執(zhí)行。但是對于事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少許不同。
PROPAGATION_MANDATORY?如果已經(jīng)存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常。
PROPAGATION_REQUIRES_NEW?總是開啟一個新的事務。如果一個事務已經(jīng)存在,則將這個存在的事務掛起。
PROPAGATION_NOT_SUPPORTED?總是非事務地執(zhí)行,并掛起任何存在的事務。
PROPAGATION_NEVER?總是非事務地執(zhí)行,如果存在一個活動事務,則拋出異常
PROPAGATION_NESTED如果一個活動的事務存在,則運行在一個嵌套的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執(zhí)行
事務是邏輯處理原子性的保證手段,通過使用事務控制,可以極大的避免出現(xiàn)邏輯處理失敗導致的臟數(shù)據(jù)等問題。
事務最重要的兩個特性,是事務的傳播級別和數(shù)據(jù)隔離級別。傳播級別定義的是事務的控制范圍,事務隔離級別定義的是事務在數(shù)據(jù)庫讀寫方面的控制范圍。
事務的7種傳播級別:
1) PROPAGATION_REQUIRED ,默認的spring事務傳播級別,使用該級別的特點是,如果上下文中已經(jīng)存在事務,那么就加入到事務中執(zhí)行,如果當前上下文中不存在事務,則新建事務執(zhí)行。所以這個級別通常能滿足處理大多數(shù)的業(yè)務場景。
2)PROPAGATION_SUPPORTS ,從字面意思就知道,supports,支持,該傳播級別的特點是,如果上下文存在事務,則支持事務加入事務,如果沒有事務,則使用非事務的方式執(zhí)行。所以說,并非所有的包在transactionTemplate.execute中的代碼都會有事務支持。這個通常是用來處理那些并非原子性的非核心業(yè)務邏輯操作。應用場景較少。
3)PROPAGATION_MANDATORY , 該級別的事務要求上下文中必須要存在事務,否則就會拋出異常!配置該方式的傳播級別是有效的控制上下文調(diào)用代碼遺漏添加事務控制的保證手段。比如一段代碼不能單獨被調(diào)用執(zhí)行,但是一旦被調(diào)用,就必須有事務包含的情況,就可以使用這個傳播級別。
4)PROPAGATION_REQUIRES_NEW ,從字面即可知道,new,每次都要一個新事務,該傳播級別的特點是,每次都會新建一個事務,并且同時將上下文中的事務掛起,執(zhí)行當前新建事務完成以后,上下文事務恢復再執(zhí)行。
這是一個很有用的傳播級別,舉一個應用場景:現(xiàn)在有一個發(fā)送100個紅包的操作,在發(fā)送之前,要做一些系統(tǒng)的初始化、驗證、數(shù)據(jù)記錄操作,然后發(fā)送100封紅包,然后再記錄發(fā)送日志,發(fā)送日志要求100%的準確,如果日志不準確,那么整個父事務邏輯需要回滾。
怎么處理整個業(yè)務需求呢?就是通過這個PROPAGATION_REQUIRES_NEW 級別的事務傳播控制就可以完成。發(fā)送紅包的子事務不會直接影響到父事務的提交和回滾。
5)PROPAGATION_NOT_SUPPORTED ,這個也可以從字面得知,not supported ,不支持,當前級別的特點就是上下文中存在事務,則掛起事務,執(zhí)行當前邏輯,結(jié)束后恢復上下文的事務。
這個級別有什么好處?可以幫助你將事務極可能的縮小。我們知道一個事務越大,它存在的風險也就越多。所以在處理事務的過程中,要保證盡可能的縮小范圍。比如一段代碼,是每次邏輯操作都必須調(diào)用的,比如循環(huán)1000次的某個非核心業(yè)務邏輯操作。這樣的代碼如果包在事務中,勢必造成事務太大,導致出現(xiàn)一些難以考慮周全的異常情況。所以這個事務這個級別的傳播級別就派上用場了。用當前級別的事務模板抱起來就可以了。
6)PROPAGATION_NEVER ,該事務更嚴格,上面一個事務傳播級別只是不支持而已,有事務就掛起,而PROPAGATION_NEVER傳播級別要求上下文中不能存在事務,一旦有事務,就拋出runtime異常,強制停止執(zhí)行!這個級別上輩子跟事務有仇。
7)PROPAGATION_NESTED ,字面也可知道,nested,嵌套級別事務。該傳播級別特征是,如果上下文中存在事務,則嵌套事務執(zhí)行,如果不存在事務,則新建事務。
那么什么是嵌套事務呢?很多人都不理解,我看過一些博客,都是有些理解偏差。
嵌套是子事務套在父事務中執(zhí)行,子事務是父事務的一部分,在進入子事務之前,父事務建立一個回滾點,叫save point,然后執(zhí)行子事務,這個子事務的執(zhí)行也算是父事務的一部分,然后子事務執(zhí)行結(jié)束,父事務繼續(xù)執(zhí)行。重點就在于那個save point??磶讉€問題就明了了:
如果子事務回滾,會發(fā)生什么?
父事務會回滾到進入子事務前建立的save point,然后嘗試其他的事務或者其他的業(yè)務邏輯,父事務之前的操作不會受到影響,更不會自動回滾。
如果父事務回滾,會發(fā)生什么?
父事務回滾,子事務也會跟著回滾!為什么呢,因為父事務結(jié)束之前,子事務是不會提交的,我們說子事務是父事務的一部分,正是這個道理。那么:
事務的提交,是什么情況?
是父事務先提交,然后子事務提交,還是子事務先提交,父事務再提交?答案是第二種情況,還是那句話,子事務是父事務的一部分,由父事務統(tǒng)一提交。
現(xiàn)在你再體會一下這個”嵌套“,是不是有那么點意思?
以上是事務的7個傳播級別,在日常應用中,通??梢詽M足各種業(yè)務需求,但是除了傳播級別,在讀取數(shù)據(jù)庫的過程中,如果兩個事務并發(fā)執(zhí)行,那么彼此之間的數(shù)據(jù)是如何影響的呢?
這就需要了解一下事務的另一個特性:數(shù)據(jù)隔離級別
數(shù)據(jù)隔離級別分為不同的四種:
1、Serializable :最嚴格的級別,事務串行執(zhí)行,資源消耗最大;
2、REPEATABLE READ :保證了一個事務不會修改已經(jīng)由另一個事務讀取但未提交(回滾)的數(shù)據(jù)。避免了“臟讀取”和“不可重復讀取”的情況,但是帶來了更多的性能損失。
3、READ COMMITTED :大多數(shù)主流數(shù)據(jù)庫的默認事務等級,保證了一個事務不會讀到另一個并行事務已修改但未提交的數(shù)據(jù),避免了“臟讀取”。該級別適用于大多數(shù)系統(tǒng)。
4、Read Uncommitted :保證了讀取過程中不會讀取到非法數(shù)據(jù)。
?
上面的解釋其實每個定義都有一些拗口,其中涉及到幾個術(shù)語:臟讀、不可重復讀、幻讀。
這里解釋一下:
?
臟讀 :所謂的臟讀,其實就是讀到了別的事務回滾前的臟數(shù)據(jù)。比如事務B執(zhí)行過程中修改了數(shù)據(jù)X,在未提交前,事務A讀取了X,而事務B卻回滾了,這樣事務A就形成了臟讀。
?
不可重復讀 :不可重復讀字面含義已經(jīng)很明了了,比如事務A首先讀取了一條數(shù)據(jù),然后執(zhí)行邏輯的時候,事務B將這條數(shù)據(jù)改變了,然后事務A再次讀取的時候,發(fā)現(xiàn)數(shù)據(jù)不匹配了,就是所謂的不可重復讀了。
?
幻讀 :小的時候數(shù)手指,第一次數(shù)十10個,第二次數(shù)是11個,怎么回事?產(chǎn)生幻覺了?
幻讀也是這樣子,事務A首先根據(jù)條件索引得到10條數(shù)據(jù),然后事務B改變了數(shù)據(jù)庫一條數(shù)據(jù),導致也符合事務A當時的搜索條件,這樣事務A再次搜索發(fā)現(xiàn)有11條數(shù)據(jù)了,就產(chǎn)生了幻讀。
?
一個對照關(guān)系表:
?????????????????????????????????????? Dirty reads????????? non-repeatable reads??????????? phantom reads
Serializable????????????????????????? 不會??????????????????????? 不會?????????????????????????????????????????? 不會
REPEATABLE READ???????????? 不會??????????????????????? 不會??????????????????????????????????????????? 會
READ COMMITTED???????????? 不會??????????????????????? 會??????????????????????????????????????????????? 會
Read Uncommitted???????????? 會?????????????????????????? 會??????????????????????????????????????????????? 會
?
所以最安全的,是Serializable,但是伴隨而來也是高昂的性能開銷。
另外,事務常用的兩個屬性:readonly和timeout
一個是設(shè)置事務為只讀以提升性能。
另一個是設(shè)置事務的超時時間,一般用于防止大事務的發(fā)生。還是那句話,事務要盡可能的小!
最后引入一個問題:
一個邏輯操作需要檢查的條件有20條,能否為了減小事務而將檢查性的內(nèi)容放到事務之外呢?
很多系統(tǒng)都是在DAO的內(nèi)部開始啟動事務,然后進行操作,最后提交或者回滾。這其中涉及到代碼設(shè)計的問題。小一些的系統(tǒng)可以采用這種方式來做,但是在一些比較大的系統(tǒng),
邏輯較為復雜的系統(tǒng)中,勢必會將過多的業(yè)務邏輯嵌入到DAO中,導致DAO的復用性下降。所以這不是一個好的實踐。
來回答這個問題:能否為了縮小事務,而將一些業(yè)務邏輯檢查放到事務外面?答案是:對于核心的業(yè)務檢查邏輯,不能放到事務之外,而且必須要作為分布式下的并發(fā)控制!
一旦在事務之外做檢查,那么勢必會造成事務A已經(jīng)檢查過的數(shù)據(jù)被事務B所修改,導致事務A徒勞無功而且出現(xiàn)并發(fā)問題,直接導致業(yè)務控制失敗。
所以,在分布式的高并發(fā)環(huán)境下,對于核心業(yè)務邏輯的檢查,要采用加鎖機制。
比如事務開啟需要讀取一條數(shù)據(jù)進行驗證,然后邏輯操作中需要對這條數(shù)據(jù)進行修改,最后提交。
這樣的一個過程,如果讀取并驗證的代碼放到事務之外,那么讀取的數(shù)據(jù)極有可能已經(jīng)被其他的事務修改,當前事務一旦提交,又會重新覆蓋掉其他事務的數(shù)據(jù),導致數(shù)據(jù)異常。
所以在進入當前事務的時候,必須要將這條數(shù)據(jù)鎖住,使用for update就是一個很好的在分布式環(huán)境下的控制手段。
一種好的實踐方式是使用編程式事務而非生命式,尤其是在較為規(guī)模的項目中。對于事務的配置,在代碼量非常大的情況下,將是一種折磨,而且人肉的方式,絕對不能避免這種問題。
將DAO保持針對一張表的最基本操作,然后業(yè)務邏輯的處理放入manager和service中進行,同時使用編程式事務更精確的控制事務范圍。
特別注意的,對于事務內(nèi)部一些可能拋出異常的情況,捕獲要謹慎,不能隨便的catch Exception 導致事務的異常被吃掉而不能正?;貪L。
Spring配置聲明式事務:
* 配置DataSource
* 配置事務管理器
* 事務的傳播特性
* 那些類那些方法使用事務
Spring配置文件中關(guān)于事務配置總是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的只是代理機制這部分。
??? DataSource、TransactionManager這兩部分只是會根據(jù)數(shù)據(jù)訪問方式有所變化,比如使用Hibernate進行數(shù)據(jù)訪問 時,DataSource實際為SessionFactory,TransactionManager的實現(xiàn)為 HibernateTransactionManager。
根據(jù)代理機制的不同,Spring事務的配置又有幾種不同的方式:
第一種方式:每個Bean都有一個代理
?第二種方式:所有Bean共享一個代理基類
第三種方式:使用攔截器
第四種方式:使用tx標簽配置的攔截器
第五種方式:全注解
1、spring事務控制放在service層,在service方法中一個方法調(diào)用service中的另一個方法,默認開啟幾個事務?
spring的事務傳播方式默認是PROPAGATION_REQUIRED,判斷當前是否已開啟一個新事務,有則加入當前事務,否則新開一個事務(如果沒有就開啟一個新事務),所以答案是開啟了一個事務。
2、spring 什么情況下進行事務回滾?
Spring、EJB的聲明式事務默認情況下都是在拋出unchecked exception后才會觸發(fā)事務的回滾
unchecked異常,即運行時異常runntimeException 回滾事務;
checked異常,即Exception可try{}捕獲的不會回滾.當然也可配置spring參數(shù)讓其回滾.
spring的事務邊界是在調(diào)用業(yè)務方法之前開始的,業(yè)務方法執(zhí)行完畢之后來執(zhí)行commit or rollback(Spring默認取決于是否拋出runtime異常).
如果拋出runtime exception 并在你的業(yè)務方法中沒有catch到的話,事務會回滾。
一般不需要在業(yè)務方法中catch異常,如果非要catch,在做完你想做的工作后(比如關(guān)閉文件等)一定要拋出runtime exception,否則spring會將你的操作commit,這樣就會產(chǎn)生臟數(shù)據(jù).所以你的catch代碼是畫蛇添足。
總結(jié)
以上是生活随笔為你收集整理的Spring五个事务隔离级别和七个事务传播行为的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java提高篇(十七)-----异常(二
- 下一篇: spring事务传播属性与隔离级别