1. 程式人生 > >spring事務詳解(一)初探討

spring事務詳解(一)初探討

上鎖 actions sha 我們 一起 很多 應用 out 得到

一、什麽是事務

維基百科:
數據庫事務(簡稱:事務)是數據庫管理系統執行過程中的一個邏輯單位,由一個有限的數據庫操作序列構成。
理解:
事務(Transaction)是數據庫區別於文件系統的重要特性之一。傳統關系型數據庫設計原則是滿足 ACID特性,用以保證數據庫事務的正確執行。Mysql的innoDB引擎就很好的支持了ACID。

技術分享圖片

二、事務的ACID特性

(箭頭後,翻譯自官網介紹:InnoDB and the ACID Model )

1,原子性(Atomicity):一個事務必須被視為一個不可分割的最小工作單元,整個事務中的所有操作要麽全部提交成功,要麽全部失敗回滾。--》主要涉及InnoDB事務。相關特性:事務的提交,回滾,信息表。

2,一致性(consistency):數據庫總是從一個一致性的狀態轉換到另一個一致性的狀態。在事務開始前後,數據庫的完整性約束沒有被破壞。例如違反了唯一性,必須撤銷事務,返回初始狀態。--》主要涉及內部InnoDB處理,以保護數據不受崩潰,相關特性:雙寫緩沖、崩潰恢復。

3,隔離性(isolation):每個讀寫事務的對象對其他事務的操作對象能相互分離,即:事務提交前對其他事務是不可見的,通常內部加鎖實現。--》主要涉及事務,尤其是事務隔離級別,相關特性:隔離級別、innodb鎖的底層實現細節。

4,持久性(durability):一旦事務提交,則其所做的修改會永久保存到數據庫。--》涉及到MySQL軟件特性與特定硬件配置的相互影響,相關特性:4個配置項:雙寫緩沖開關、事務提交刷新log的級別、binlog同步頻率、表文件;寫緩存、操作系統對於fsync()的支持、備份策略等。

The database remains in a consistent state at all times — after each commit or rollback, and while transactions are in progress.

If related data is being updated across multiple tables, queries see either all old values or all new values, not a mix of old and new values.(摘自MYSQL官網ACID中一致性狀態的描述)


數據庫在事務的提交、回滾,或運行中,總是處於一個一致的狀態。

如果關聯數據跨多張表更新,查詢時只能看見全老數據/全新數據,而不是新老數據混合。

The consistency aspect of the ACID model mainly involves internal InnoDB processing to protect data from crashes.

Related MySQL features include: InnoDB doublewrite buffer. InnoDB crash recovery. (摘自MYSQL-一致性特性)
ACID模型的一致性方面主要涉及內部InnoDB處理,以保護數據不受崩潰影響。MySQL相關特性包括: 雙寫緩沖、崩潰恢復。

三、事務的屬性

要保證事務的ACID特性,spring給事務定義了6個屬性,對應於聲明式事務註解(org.springframework.transaction.annotation.Transactional)@Transactional(key1=*,key2=*...)

事務名稱:用戶可手動指定事務的名稱,當多個事務的時候,可區分使用哪個事務。對應註解中的屬性value、transactionManager
隔離級別: 為了解決數據庫容易出現的問題,分級加鎖處理策略。 對應註解中的屬性isolation
超時時間: 定義一個事務執行過程多久算超時,以便超時後回滾。可以防止長期運行的事務占用資源.對應註解中的屬性timeout
是否只讀:表示這個事務只讀取數據但不更新數據, 這樣可以幫助數據庫引擎優化事務.對應註解中的屬性readOnly
傳播機制: 對事務的傳播特性進行定義,共有7種類型。對應註解中的屬性propagation
回滾機制:定義遇到異常時回滾策略。對應註解中的屬性rollbackFor、noRollbackFor、rollbackForClassName、noRollbackForClassName
其中隔離級別和傳播機制比較復雜,咱們細細地品一品

3.1 隔離級別

這一塊比較復雜,我們從3個角度來看:3種錯誤現象、mysql的底層技術支持、分級處理策略。這一小節一定要好好看,已經開始涉及核心原理了。

1.現象(三種問題)
臟讀(Drity Read):事務A更新記錄但未提交,事務B查詢出A未提交記錄。

不可重復讀(Non-repeatable read): 事務A讀取一次,此時事務B對數據進行了更新或刪除操作,事務A再次查詢數據不一致。

幻讀(Phantom Read): 事務A讀取一次,此時事務B插入一條數據,事務A再次查詢,記錄多了。

2. mysql的底層支持(IndoDB事務模型)(一致性非鎖定讀VS鎖定讀)
官網飛機票:InnoDB Transaction Model

兩種讀
在MVCC中,讀操作可以分成兩類,快照讀(Snapshot read)和當前讀(current read)。

快照讀:普通的select * from

當前讀:

select * from table where ? lock in share mode; (加S鎖)
select * from table where ? for update; (加X鎖)
insert, update, delete 操作前會先進行一次當前讀(加X鎖)
其中前兩種鎖定讀,需要用戶自己顯式使用,最後一種是自動添加的。

   1.一致性非鎖定讀(快照讀)

一致性非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎通過多版本控制(multi versionning)的方式來讀取當前執行時間數據庫中行的數據,如果讀取的行正在執行DELETE或UPDATE操作,這時讀取操作不會因此等待行上鎖的釋放。相反的,InnoDB會去讀取行的一個快照數據

技術分享圖片

上面展示了InnoDB存儲引擎一致性的非鎖定讀。之所以稱為非鎖定讀,因為不需要等待訪問的行上X鎖的釋放。快照數據是指該行之前版本的數據,該實現是通過undo段來完成。而undo用來事務中的回滾數據,因此快照數據本身沒有額外的開銷,此外,讀取快照數據不需要上鎖,因為沒有事務需要對歷史數據進行修改操作。

   2.鎖定讀

三種鎖
1,Record Lock(行級鎖):
1)共享鎖:S Lock,允許事務讀取一行數據。
2)排它鎖:X Lock,允許事務刪除或更新一行數據。
註:只有S+S是兼容的,其它3種組合都是不兼容的。

2,Gap Lock(間隙鎖):鎖定一個範圍,不包含記錄本身。

3,Next-Key Lock(下一鍵鎖):Gap Lock + Record Lock,鎖定範圍和記錄本身。

innoDB對select語句支持兩種鎖定讀:

1)SELECT...FOR UPDATE:對讀取的行加排它鎖(X鎖),其他事務不能對已鎖定的行再加任何鎖。

2 ) SELECT...LOCK IN SHARE MODE :對讀取的行加共享鎖(S鎖),其他事務可以再加S鎖,X鎖會阻塞等待。

註:這兩種鎖都必須處於事務中,事務commit,鎖釋放。所以必須begin或者start transaction 開啟一個事務或者索性set autocommit=0把自動提交關掉(mysql默認是1,即執行完sql立即提交)

3. 分級處理策略(四種隔離級別)
官網描述:

InnoDB使用不同的鎖定策略支持每個事務隔離級別。對於關鍵數據的操作(遵從ACID原則),您可以使用強一致性(默認Repeatable Read)。對於不是那麽重要的數據操作,可以使用Read Committed/Read Uncommitted。Serializable執行比可重讀更嚴格的規則,用於特殊場景:XA事務,並發性和死鎖問題的故障排除。

四種隔離級別:

1.Read Uncommitted(讀未提交內容):可能讀取其它事務未提交的數據。-臟讀問題(臟讀+不可重復讀+幻讀)

2.Read Committed(讀已提交內容):一個事務只能看見已經提交事務所做的改變。其它事務commit,當前事務中多次查詢可能返回不同結果。(不可重復讀+幻讀)

select...from : 一致性非鎖定讀的數據快照(MVCC)是最新版本的,但其他事務可能會有新的commit,所以同一select可能返回不同結果。-不可重復讀問題
select...from for update : record lock行級鎖.

3.Repeatable Read(可重讀):

select…from :同一事務內多次一致性非鎖定讀,取第一次讀取時建立的快照版本(MVCC),保證了同一事務內部的可重復讀.—狹義的幻讀問題得到解決。(Db插入了數據,只不過讀不到)

select...from for update (FOR UPDATE or LOCK IN SHARE MODE), UPDATE, 和 DELETE : next-key lock下一鍵鎖.

  1)對於具有唯一搜索條件的唯一索引,innoDB只鎖定找到的索引記錄. (next-key lock 降為record lock)

  2)對於其他非索引或者非唯一索引,InnoDB會對掃描的索引範圍進行鎖定,使用next-key locks,阻塞其他session對間隙的insert操作,-徹底解決廣義的幻讀問題。(DB沒插入數據)

4.Serializable(可串行化):這是最高的隔離級別,它是在每個讀的數據行上加上共享鎖(LOCK IN SHARE MODE)。在這個級別,可能導致大量的超時現象和鎖競爭,主要用於分布式事務。

如下表:

技術分享圖片

3.2 傳播機制

org.springframework.transaction包下有一個事務定義接口TransactionDefinition,定義了7種事務傳播機制,很多人對傳播機制的曲解從概念開始,所以特地翻譯了一下源碼註釋如下:

關於EJB事務,可參考,EJB 是用於組件開發的一個框架,EJB 架構支持分布式事務。

1.PROPAGATION_REQUIRED

支持當前事務;如果不存在,創建一個新的。類似於同名的EJB事務屬性。這通常是事務定義的默認設置,通常定義事務同步作用域。

2.PROPAGATION_SUPPORTS

支持當前事務;如果不存在事務,則以非事務方式執行。類似於同名的EJB事務屬性。

註意:對於具有事務同步的事務管理器,PROPAGATION_SUPPORTS與沒有事務稍有不同,因為它可能在事務範圍內定義了同步。因此,相同的資源(JDBC的Connection、Hibernate的Session等)將在整個指定範圍內共享。註意,確切的行為取決於事務管理器的實際同步配置!

小心使用PROPAGATION_SUPPORTS!特別是,不要依賴PROPAGATION_REQUIRED或PROPAGATION_REQUIRES_NEW,在PROPAGATION_SUPPORTS範圍內(這可能導致運行時的同步沖突)。如果這種嵌套不可避免,請確保適當地配置事務管理器(通常切換到“實際事務上的同步”)。

3.PROPAGATION_MANDATORY

支持當前事務;如果當前事務不存在,拋出異常。類似於同名的EJB事務屬性。

註意:PROPAGATION_MANDATORY範圍內的事務同步總是由周圍的事務驅動。

4.PROPAGATION_REQUIRES_NEW

創建一個新事務,如果存在當前事務,則掛起當前事務。類似於同名的EJB事務屬性。

註意:實際事務掛起不會在所有事務管理器上開箱即用。這一點特別適用於JtaTransactionManager,它需要TransactionManager的支持。

PROPAGATION_REQUIRES_NEW範圍總是定義自己的事務同步。現有同步將被掛起並適當地恢復。

5.PROPAGATION_NOT_SUPPORTED

不支持當前事務,存在事務掛起當前事務;始終以非事務方式執行。類似於同名的EJB事務屬性。

註意:實際事務掛起不會在所有事務管理器上開箱即用。這一點特別適用於JtaTransactionManager,它需要TransactionManager的支持。

事務同步在PROPAGATION_NOT_SUPPORTED範圍內是不可用的。現有同步將被掛起並適當地恢復。

6.PROPAGATION_NEVER

不支持當前事務;如果當前事務存在,拋出異常。類似於同名的EJB事務屬性。

註意:事務同步在PROPAGATION_NEVER範圍內不可用。

7.PROPAGATION_NESTED

如果當前事務存在,則在嵌套事務中執行,如果當前沒有事務,類似PROPAGATION_REQUIRED(創建一個新的)。EJB中沒有類似的功能。

註意:實際創建嵌套事務只對特定的事務管理器有效。開箱即用,這只適用於 DataSourceTransactionManager(JDBC 3.0驅動)。一些JTA提供者也可能支持嵌套事務。

註意:NESTED和REQUIRES_NEW區別?
1.回滾:
NESTED在創建內層事務之前創建一個保存點,內層事務回滾只回滾到保存點,不會影響外層事務(真的可以自動實現嗎?)。外層事務回滾則會連著內層事務一起回滾;
REQUIRES_NEW構造一個新事務,和外層事務是兩個獨立的事務,互不影響。

2.提交:NESTED是嵌套事務,是外層事務的子事務。外層事務commit則內部事務一起提交,只有一次commit;REQUIRES_NEW是新事務,完全獨立的事務,獨立進行2次commit。

註:
NESTED嵌套事務能夠自己回滾到保存點,但是嵌套事務方法中的上拋的異常,外部方法也能捕獲,那麽外部事務也就回滾了,所以如果期望實現內部嵌套異常回滾不影響外部事務,那麽需要捕獲嵌套事務的異常,並保證吃掉異常,不再上拋。

四、總結

本節講解了事務的4大特性和6大屬性的概念。

註意:JtaTransactionManager的類註釋上說:Transaction suspension (REQUIRES_NEW, NOT_SUPPORTED) is just available with a JTA TransactionManager being registered." 這是片面的,只是說JTA TransactionManager支持掛起,並沒有說DataSourceTransactionManager不支持。經過第四節實測,發現完全是支持的。網上很多說REQUIRES_NEW、NOT_SUPPORTED必須要JTA TransactionManager才行的完全是錯誤的說法。

不同傳播機制 事務名稱 描述 事務管理器要求 是否支持事務 是否開啟新事務 回滾規則

REQUIRED

要求

存在加入,不存在創建新

?

不一定

存在一個事務:1.外部有事務加入,異常回滾;2.外部沒事務創建新事務,異常回滾

SUPPORTS

支持

存在加入,不存在非事務

?

?

最多只存在一個事務: 1.外部有事務加入,異常回滾;2.外部沒事務,內部非事務,異常不回滾

MANDATORY

強制

存在加入,不存在拋異常

?

?

最多只存在一個事務: 1.外部存在事務加入,異常回滾;2.外部不存在事務,異常無法回滾

REQUIRES_NEW

要求新

存在掛起創建新,不存在創建新

?

?

可能存在1-2個事務:1.外部存在事務掛起,創建新,異常回滾自己的事務 2.外部不存在事務,創建新, 異常只回滾新事務

NOT_SUPPORTED

不支持

存在掛起,不存在非事務

?

?

最多只存在一個事務:1. 外部有事務掛起,外部異常回滾;內部非事務,異常不回滾2.外部無事務,內部非事務,異常不回滾

NEVER

堅決不

存在拋異常

?

?

最多只存在一個事務:1.外部有事務,外部異常回滾;內部非事務不回滾 2.外部非事務,內部非事務,異常不回滾

NESTED

嵌套

存在嵌套,不存在創建新

DataSourceTransactionManager

?

?(同一個物理事務,保存點實現嵌套)

存在一個事務:1. 外部有事務,嵌套事務創建保存點,外部異常回滾全部事務;內部嵌套事務異常回滾到保存點;2.外部不存在事務,內部創建新事務,內部異常回滾

參考書籍《精通Spring4.x企業應用開發實戰》

參考博文鏈接:https://www.cnblogs.com/dennyzhangdd/p/9549535.html

spring事務詳解(一)初探討