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