繼續卷!面試又問Spring 事務有幾種傳播行為和隔離級別?
面試又被問到了事務,來吧,要么卷起來,要么躺平。卷不動躺平會不會導致數據不一致?
事務概念
事務是數據庫管理系統執行過程中的一個邏輯單位,由一個有限的數據庫操作序列構成。
說簡單點就是,要么所有執行success,不然就fail。它最終的目標:數據不會被破壞。即事務操作成功,數據的結果和業務期待的結果是一致的。
事務的屬性
一個邏輯工作單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性
- 原子性(Atomicity)
- 一致性(Consistency)
- 隔離性(Isolation)
- 持久性(Durability)
1:原子性(Atomicity):原子性要求事務作為一個不可分割的整體被執行,包含在其中的對數據庫的操作要么全部被執行,要么都不執行(其中有一個操作失敗,就全部失敗)。
2:一致性(Consistency):一致性要求事務應確保數據庫的狀態從一個一致狀態轉變為另一個一致狀態。一致狀態的含義是數據庫中的數據應滿足完整性約束。
執行前數據間的一致性狀態 === 執行后數據間的一致性狀態
3:隔離性(Isolation):事務的隔離性要求多個事務并發執行時,一個事務的執行不應影響其他事務的執行。
4:持久性(Durability):事務的持久性是一旦整個事務提交成功,數據的修改應該永久保存在數據庫中,并不可逆轉。
隔離性(Isolation)
事務指定了4種隔離級別(從弱到強分別是):
- Read Uncommitted
- Read Committed
- Repeatable Read
- Serializable
在事務的并發操作中可能會出現臟讀(dirty read),不可重復讀(repeatable read),幻讀(phantom read)。
1:Read Uncommitted(讀未提交):一個事務可以讀取另一個未提交事務的數據。
2:Read Committed(讀提交):一個事務要等另一個事務提交后才能讀取數據。
3:Repeatable Read(重復讀):在開始讀取數據(事務開啟)時,不再允許修改操作。
4:Serializable(序列化):Serializable 是最高的事務隔離級別,在該級別下,事務串行化順序執行,可以避免臟讀、不可重復讀與幻讀。
大多數數據庫默認的事務隔離級別是Read committed,比如Sql Server , Oracle。MySQL的默認隔離級別是Repeatable read。
Spring事務的傳播性
事務的傳播級別和數據隔離級別,是事務控制的兩個主要特性。傳播級別定義的是事務的控制范圍,事務隔離級別定義的是事務在數據庫讀寫方面的控制范圍。
Spring事務傳播性有七種,REQUIRED、SUPPORTS、REQUIRES-NEW、NOT-SUPPORTED、MANDATORY、NEVER、NESTED。如下思維導圖:
Spring事務的傳播特性介紹:
- PROPAGATION_REQUIRED: 如果存在一個事務,則支持當前事務。如果沒有事務則開啟新的事物。
- PROPAGATION_SUPPORTS: 如果存在一個事務,支持當前事務。如果沒有事務,則非事務的執行。
- PROPAGATION_MANDATORY: 如果已經存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常。
- PROPAGATION_REQUIRES_NEW: 總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。
- PROPAGATION_NOT_SUPPORTED: 總是非事務地執行,并掛起任何存在的事務。
- PROPAGATION_NEVER: 總是非事務地執行,如果存在一個活動事務,則拋出異常
- 7.(spring)PROPAGATION_NESTED:如果一個活動的事務存在,則運行在一個嵌套的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。
Spring事務傳播特性總結:
- 1.只要定義為spring的bean就可以對里面的方法使用@Transactional注解。
- 2.Spring的事務傳播是Spring特有的。不是對底層jdbc的代理。
- 3.使用spring聲明式事務,spring使用AOP來支持聲明式事務,會根據事務屬性,自動在[方法調用之前決定是否開啟一個事務],并在[方法執行之后]決定事務提交或回滾事務。
- 4.Spring支持的PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的區別: PROPAGATION_REQUIRES_NEW:二個事務沒有信賴關系,不會存在A事務的成功取決于B事務的情況。有可能存在A提交B失敗。A失敗(比如執行到doSomeThingB的時候拋出異常)B提交,AB都提交,AB都失敗的可能。PROPAGATION_NESTED:與PROPAGATION_REQUIRES_NEW不同的是,內嵌事務B會信賴A。即存在A失敗B失敗。A成功,B失敗。A成功,B成功。而不存在A失敗,B成功。
- 5.特別注意PROPAGATION_NESTED的使用條件:使用JDBC 3.0驅動時,僅僅支持DataSourceTransactionManager作為事務管理器。需要JDBC 驅動的java.sql.Savepoint類。有一些JTA的事務管理器實現可能也提供了同樣的功能。使用PROPAGATION_NESTED,還需 要把PlatformTransactionManager的nestedTransactionAllowed屬性設為true;而 nestedTransactionAllowed屬性值默認為false;
- 6.特別注意PROPAGATION_REQUIRES_NEW的使用條件:JtaTransactionManager作為事務管理器
Spring事務的隔離級別?
Spring事務的隔離級別:
- ISOLATION_DEFAULT:這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別。
- ISOLATION_READ_UNCOMMITTED:這是事務最低的隔離級別,它充許令外一個事務可以看到這個事務未提交的數據。
- ISOLATION_READ_COMMITTED:保證一個事務修改的數據提交后才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據。
- ISOLATION_REPEATABLE_READ:這種事務隔離級別可以防止臟讀,不可重復讀。但是可能出現幻像讀。它除了保證一個事務不能讀取另一個事務未提交的數據外,還保證了避免下面的情況產生(不可重復讀)。
- ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。除了防止臟讀,不可重復讀外,還避免了幻像讀。除了第一個是spring特有的,另外四個與JDBC的隔離級別相對應。第二種隔離級別會產生臟讀,不可重復讀和幻像讀,特別是臟讀,一般情況下 是不允許的,所以這種隔離級別是很少用到的。大多說數據庫的默認格里基本是第三種。它能消除臟讀,但是可重復讀保證不了。第四種隔離級別也有一些數據庫作 為默認的隔離級別,比如MySQL。最后一種用的地方不多,除非是多數據訪問的要求特別高,否則輕易不要用它,因為它會嚴重影響數據庫的性能
Spring事務的架構?
Spring 的事務框架設計理念的基本原則是:讓事務管理的關注點與數據訪問關注點相分離。
架構
Spring 的事務抽象包括3個主要接口,分別是PlatformTransactionManager、TransactionDefinition、TransactionSatus。
- PlatformTransactionManager負責界定事務邊界;TransactionDefinition負責定義事務的相關屬性,包括隔離級別、傳播行為等;PlatformTransactionManager參照TransactionDefinition的屬性定義來開啟相關事務。事務開啟之后到事務結束期間的事務狀態由TransactionStatus負責,我們可以通過TransactionStatus對事務進行有限的控制。
- TransactionDefinition常用的實現有DefaultTransactionDefinition和TransactionTemplate(這兩個主要用于編程式的事務場景)、DefaultTransactionAttribute和RuleBasedTransactionAttribute(這兩個主要使用Spring AOP 進行聲明式事務管理的場景中,RuleBasedTransactionAttribute允許我們同時制定多個回滾規則)。
- TransactionStatus有一個實現類DefaultTransactionStatus用來記錄事務的狀態信息。PlatformTransactionManager的實現類可以分為面向局部事務和面向全局事務兩個分支。常用的面向局部事務的PlatformTransactionManager有DataSourceTransactionManager(用于JDBC和Mybatis)和HibernateTransactionManager。
使用Spring如何進行事務管理?
事務管理配置
- 編程式事務
- 聲明式事務
編程式事務使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對于編程式事務管理,spring推薦使用TransactionTemplate。
聲明式事務是建立在AOP之上的。其本質是對方法前后進行攔截,然后在目標方法開始之前創建或者加入一個事務,在執行完目標方法之后根據執行情況提交或者回滾事務。聲明式事務最大的優點就是不需要通過編程的方式管理事務,這樣就不需要在業務邏輯代碼中摻雜事務管理的代碼,只需在配置文件中做相關的事務規則聲明(或通過基于@Transactional注解的方式),便可以將事務規則應用到業務邏輯中。
顯然聲明式事務管理要優于編程式事務管理,這正是spring倡導的非侵入式的開發方式。聲明式事務管理使業務代碼不受污染,一個普通的POJO對象,只要加上注解就可以獲得完全的事務支持。和編程式事務相比,聲明式事務唯一不足地方是,它的最細粒度只能作用到方法級別,無法做到像編程式事務那樣可以作用到代碼塊級別。但是即便有這樣的需求,也存在很多變通的方法,比如,可以將需要進行事務管理的代碼塊獨立為方法等等。
聲明式事務管理也有兩種常用的方式,一種是基于tx和aop名字空間的xml配置文件,另一種就是基于@Transactional注解。顯然基于注解的方式更簡單易用,更清爽。
本文轉載自微信公眾號「Java編程技術樂園」,可以通過以下二維碼關注。轉載本文請聯系Java編程技術樂園公眾號。