1- 事務ACID
事務由一系列操作組成的,保證所有操作整體原子執(zhí)行,完整的事務滿足ACID特性
原子性(Atomicity):事務是一個原子操作侥猬,由一系列動作組成衍菱。事務的原子性確保動作要么全部完成,要么完全不起作用隘击。
一致性(Consistency):一旦事務完成(不管成功還是失敗)研铆,系統(tǒng)必須確保它所建模的業(yè)務處于一致的狀態(tài)埋同,而不會是部分完成部分失敗。在現(xiàn)實中的數(shù)據(jù)不應該被破壞棵红。
隔離性(Isolation):可能有許多事務會同時處理相同的數(shù)據(jù)凶赁,因此每個事務都應該與其他事務隔離開來,防止數(shù)據(jù)損壞逆甜。
持久性(Durability):一旦事務完成虱肄,無論發(fā)生什么系統(tǒng)錯誤,它的結果都不應該受到影響交煞,這樣就能從任何系統(tǒng)崩潰中恢復過來咏窿。通常情況下,事務的結果被寫到持久化存儲器中错敢。
2- 事務管理器
Spring并不直接管理事務翰灾,而是提供了多種事務管理器缕粹,他們將事務管理的職責委托給Hibernate或者JTA等持久化機制所提供的相關平臺框架的事務來實現(xiàn)。
2.1- PlatformTransactionManager
org.springframework.transaction.PlatformTransactionManager是Spring事務管理器的接口纸淮,通過這個接口平斩,Spring為各個平臺如JDBC、Hibernate等都提供了對應的事務管理器咽块,但是具體的實現(xiàn)就是各個平臺自己的事情了绘面。
PlatformTransactionManager接口
提供了獲取TransactionDefinition的方法以及根據(jù)TransactionStatus相應的狀態(tài)進行提交或者回滾
TransactionStatus事務狀態(tài)接口
上面講到的調用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一個實現(xiàn),這個接口的內容如下:
TransactionDefinition事務定義的接口
而TransactionDefinition接口內容如下:
屬性:
方法:
3- TransactionDefinition接口定義的事務的五個屬性
3.1- 傳播行為
當事務方法被另一個事務方法調用時侈沪,必須指定事務應該如何傳播揭璃,沒有默認值。例如:方法可能繼續(xù)在現(xiàn)有事務中運行亭罪,也可能開啟一個新事務瘦馍,并在自己的事務中運行
我們可以看 org.springframework.transaction.annotation.Propagation 枚舉類中定義了6個表示傳播行為的枚舉值:
- REQUIRED :如果當前存在事務,則加入該事務应役;如果當前沒有事務情组,則創(chuàng)建一個新的事務。
- SUPPORTS :如果當前存在事務箩祥,則加入該事務院崇;如果當前沒有事務,則以非事務的方式繼續(xù)運行袍祖。
- MANDATORY :如果當前存在事務底瓣,則加入該事務;如果當前沒有事務蕉陋,則拋出異常捐凭。
- REQUIRES_NEW :創(chuàng)建一個新的事務,如果當前存在事務寺滚,則把當前事務掛起柑营。
- NOT_SUPPORTED :以非事務方式運行屈雄,如果當前存在事務村视,則把當前事務掛起。
- NEVER :以非事務方式運行酒奶,如果當前存在事務蚁孔,則拋出異常。
- NESTED :如果當前存在事務惋嚎,則創(chuàng)建一個事務作為當前事務的嵌套事務來運行杠氢;如果當前沒有事務,則該取值等價于 REQUIRED 另伍。
指定方法:通過使用 propagation 屬性設置鼻百,例如:
@Transactional(propagation = Propagation.REQUIRED)
3.2- 隔離級別
隔離級別是指若干個并發(fā)的事務之間的隔離程度绞旅,與我們開發(fā)時候主要相關的場景包括:臟讀取、重復讀温艇、幻讀因悲。
并發(fā)事務引起的問題
在典型的應用程序中,多個事務并發(fā)運行勺爱,經(jīng)常會操作相同的數(shù)據(jù)來完成各自的任務晃琳。并發(fā)雖然是必須的,但可能會導致一下的問題琐鲁。
- 臟讀(Dirty reads)——臟讀發(fā)生在一個事務讀取了另一個事務改寫但尚未提交的數(shù)據(jù)時卫旱。如果改寫在稍后被回滾了,那么第一個事務獲取的數(shù)據(jù)就是無效的围段。
- 不可重復讀(Nonrepeatable read)——不可重復讀發(fā)生在一個事務執(zhí)行相同的查詢兩次或兩次以上顾翼,但是每次都得到不同的數(shù)據(jù)時。這通常是因為另一個并發(fā)事務在兩次查詢期間進行了更新奈泪。
- 幻讀(Phantom read)——幻讀與不可重復讀類似暴构。它發(fā)生在一個事務(T1)讀取了幾行數(shù)據(jù),接著另一個并發(fā)事務(T2)插入了一些數(shù)據(jù)時段磨。在隨后的查詢中取逾,第一個事務(T1)就會發(fā)現(xiàn)多了一些原本不存在的記錄。
不可重復讀與幻讀的區(qū)別
- 不可重復讀的重點是修改:
同樣的條件, 你讀取過的數(shù)據(jù), 再次讀取出來發(fā)現(xiàn)值不一樣了 苹支,在一個事務中前后兩次讀取的結果并不一致砾隅,導致了不可重復讀。 - 幻讀的重點在于新增或者刪除:
同樣的條件, 第1次和第2次讀出來的記錄數(shù)不一樣 - 從總的結果來看, 似乎不可重復讀和幻讀都表現(xiàn)為兩次讀取的結果不一致债蜜。但如果你從控制的角度來看, 兩者的區(qū)別就比較大:
對于前者, 只需要鎖住滿足條件的記錄晴埂。
對于后者, 要鎖住滿足條件及其相近的記錄。
我們可以看 org.springframework.transaction.annotation.Isolation 枚舉類中定義了五個表示隔離級別的值:
- DEFAULT :這是默認值寻定,表示使用底層數(shù)據(jù)庫的默認隔離級別儒洛。對大部分數(shù)據(jù)庫而言,通常這值就是: READ_COMMITTED 狼速,Mysql為REPEATABLE_READ琅锻。
- READ_UNCOMMITTED :該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數(shù)據(jù)。該級別不能防止臟讀和不可重復讀向胡,因此很少使用該隔離級別恼蓬。
- READ_COMMITTED :該隔離級別表示一個事務只能讀取另一個事務已經(jīng)提交的數(shù)據(jù)。該級別可以防止臟讀僵芹,這也是大多數(shù)情況下的推薦值处硬。
- REPEATABLE_READ :該隔離級別表示一個事務在整個過程中可以多次重復執(zhí)行某個查詢,并且每次返回的記錄都相同拇派。即使在多次查詢之間有新增的數(shù)據(jù)滿足該查詢荷辕,這些新增的記錄也會被忽略凿跳。該級別可以防止臟讀和不可重復讀。
- SERIALIZABLE :所有的事務依次逐個執(zhí)行疮方,這樣事務之間就完全不可能產生干擾拄显,也就是說,該級別可以防止臟讀案站、不可重復讀以及幻讀躬审。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別蟆盐。
指定方法:通過使用 isolation 屬性設置承边,例如:
@Transactional(isolation = Isolation.DEFAULT)
3.3- 只讀
事務的第三個特性是它是否為只讀事務。如果事務只對后端的數(shù)據(jù)庫進行該操作石挂,數(shù)據(jù)庫可以利用事務的只讀特性來進行一些特定的優(yōu)化博助。通過將事務設置為只讀,你就可以給數(shù)據(jù)庫一個機會痹愚,讓它應用它認為合適的優(yōu)化措施富岳。
3.4- 事務超時
為了使應用程序很好地運行,事務不能運行太長的時間拯腮。因為事務可能涉及對后端數(shù)據(jù)庫的鎖定窖式,所以長時間的事務會不必要的占用數(shù)據(jù)庫資源。事務超時就是事務的一個定時器动壤,在特定時間內事務如果沒有執(zhí)行完畢萝喘,那么就會自動回滾,而不是一直等待其結束琼懊。
3.5- 回滾規(guī)則
事務五邊形的最后一個方面是一組規(guī)則阁簸,這些規(guī)則定義了哪些異常會導致事務回滾而哪些不會。默認情況下哼丈,事務只有遇到運行期異常Unchecked時才會回滾启妹,而在遇到檢查型異常時不會回滾(這一行為與EJB的回滾行為是一致的)
但是你可以聲明事務在遇到特定的檢查型異常時像遇到運行期異常那樣回滾。同樣醉旦,你還可以聲明事務遇到特定的異常不回滾饶米,即使這些異常是運行期異常。
4- Spring Boot 基于聲明式注解事務使用示例
在Spring Boot中髓抑,當我們使用了spring-boot-starter-jdbc或spring-boot-starter-data-jpa依賴的時候咙崎,框 架會自動默認分別注入DataSourceTransactionManager或JpaTransactionManager优幸。所以我們不需要任何額外 配置就可以用@Transactional注解進行事務的使用吨拍。關于事務管理器,不管是JPA還是JDBC等都實現(xiàn)自接口 PlatformTransactionManager 如果你添加的是 spring-boot-starter-jdbc 依賴网杆,框架會默認注入 DataSourceTransactionManager 實例羹饰。如果你添加的是 spring-boot-starter-data-jpa 依賴伊滋,框架會默認注入 JpaTransactionManager 實例。
4.1- 事務管理兩種方式
spring支持編程式事務管理和聲明式事務管理兩種方式队秩。
編程式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager笑旺。對于編程式事務管理,spring推薦使用TransactionTemplate馍资。
聲明式事務管理建立在AOP之上的筒主。其本質是對方法前后進行攔截,然后在目標方法開始之前創(chuàng)建或者加入一個事務鸟蟹,在執(zhí)行完目標方法之后根據(jù)執(zhí)行情況提交或者回滾事務乌妙。聲明式事務最大的優(yōu)點就是不需要通過編程的方式管理事務,這樣就不需要在業(yè)務邏輯代碼中摻雜事務管理的代碼建钥,只需在配置文件中做相關的事務規(guī)則聲明(或通過基于@Transactional注解的方式)藤韵,便可以將事務規(guī)則應用到業(yè)務邏輯中。
4.2- 單事務管理開啟與配置
在啟動類中配置事務管理器
- 默認事務管理器熊经,只需開啟事務管理器即可泽艘,不需顯示的配置
- 顯示的指明事務管理器
如果你項目做的比較大,添加的持久化依賴比較多镐依,我們還是會選擇人為的指定使用哪個事務管理器
在Spring容器中匹涮,我們手工注解@Bean 將被優(yōu)先加載,框架不會重新實例化其他的 PlatformTransactionManager 實現(xiàn)類槐壳。
4.3- 多事務管理器的配置
對于同一個工程中存在多個事務管理器進行配置
啟動類實現(xiàn)TransactionManagementConfigurer接口焕盟,通過@Bean注解注入兩個事務管理器,實現(xiàn)annotationDrivenTransactionManager接口方法宏粤,指定value缺省值情況下提供的默認事務管理器脚翘,如下:
注:
如果Spring容器中存在多個 PlatformTransactionManager 實例,并且沒有實現(xiàn)接口 TransactionManagementConfigurer 指定默認值绍哎,在我們在方法上使用注解 @Transactional 的時候来农,就必須要用value指定,如果不指定崇堰,則會拋出異常沃于。
4.4- @Transaction 注解使用的注意事項
@Transactional屬性
noRollback示例
@Transactional 可以作用于接口、接口方法海诲、類以及類方法上繁莹。當作用于類上時,該類的所有 public 方法將都具有該類型的事務屬性特幔,同時咨演,我們也可以在方法級別使用該標注來覆蓋類級別的定義。
最佳實踐:
雖然 @Transactional 注解可以作用于接口蚯斯、接口方法薄风、類以及類方法上饵较,但是 Spring 建議不要在接口或者接口方法上使用該注解,因為這只有在使用基于接口的代理時它才會生效遭赂。
@Transactional 注解應該只被應用到 public 方法上循诉,這是由 Spring AOP 的本質決定的。如果你在 protected撇他、private 或者默認可見性的方法上使用 @Transactional 注解茄猫,這將被忽略,也不會拋出任何異常困肩。
默認情況下募疮,只有來自外部的方法調用才會被AOP代理捕獲,也就是僻弹,類內部方法調用本類內部的其他方法并不會引起事務行為阿浓,即使被調用方法使用@Transactional注解進行修飾。
事務異常排查列表:
- 數(shù)據(jù)庫本身是否支持事務蹋绽,如mysql的myisam引擎就不支持事務
- 多數(shù)據(jù)源芭毙,是否指定了正確的事務管理器
- 數(shù)據(jù)源配置是否正確(MyBatis的SqlSessionFactoryBean引用的數(shù)據(jù)源與DataSourceTransactionManager引用的數(shù)據(jù)源是否一致)
- 私有方法不會生效事務(AOP默認不代理私有方法,也不會拋出異常)
- 被調用方法不會生效事務(this指針調用方法卸耘,并沒有使用AOP代理來執(zhí)行方法)
- 不指明回滾的異常退敦,方法內用戶拋出的checked異常不會生效回滾(默認只有unchecked異常發(fā)生回滾)
MyBatis自動參與到spring事務管理中,無需額外配置蚣抗,只要org.mybatis.spring.SqlSessionFactoryBean引用的數(shù)據(jù)源與DataSourceTransactionManager引用的數(shù)據(jù)源一致即可侈百,否則事務管理會不起作用。
@Transaction示例
方法上注解屬性會覆蓋類注解上的相同屬性
事務管理器保證事務方法內的SQL在同一個SqlSession中翰铡,相當于:
參考鏈接
Spring聲明式事務為何不回滾