spring事務詳解(一)初探事務

系列目錄

spring事務詳解(一)初探事務html

spring事務詳解(二)簡單樣例mysql

spring事務詳解(三)源碼詳解spring

spring事務詳解(四)測試驗證sql

spring事務詳解(五)總結提升數據庫

引子

不少coder在不理解事務的原理甚至連基本概念都不清楚的狀況下,就去使用數據庫事務,是極容易出錯,寫出一些本身不能掌控的代碼。網上不少文章要不就是概念,或者一點源碼,或者一點測試驗證,都不足以全面瞭解事務,因此本文出現了,本系列Spring事務詳解包含四部分:緩存

第一章 講概念,對事務的總體有一個瞭解。session

第二章 簡單樣例,瞭解如何用。併發

第三章 從源碼來看底層實現機制。數據庫設計

第四章 實例測試驗證。分佈式

第五章 總結提升。

我的能力有限,有任何不當之處,麻煩指出。

全文基於Mysql innodb引擎。Mysql官方文檔:官網飛機票 ,推薦書籍:《Mysql技術內幕-InnoDB存儲引擎》。

1、背景

1.1 拜神

spring事務領頭人叫Juergen Hoeller,于爾根·糊了...先混個臉熟哈,他寫了幾乎所有的spring事務代碼。讀源碼先拜神,掌握他的源碼的風格,讀起來會通暢不少。最後一節我們總結下這個大神的代碼風格。

1.2 事務的定義

事務(Transaction)是數據庫區別於文件系統的重要特性之一。目前國際承認的數據庫設計原則是ACID特性,用以保證數據庫事務的正確執行。Mysql的innodb引擎中的事務就徹底符合ACID特性。

spring對於事務的支持,分層概覽圖以下:

2、事務的ACID特性

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

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

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

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

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

3、事務的屬性

要保證事務的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

當前讀

  1. select * from table where ? lock in share mode; (加S鎖)
  2. select * from table where ? for update; (加X鎖)
  3. insert, update, delete 操做前會先進行一次當前讀(加X鎖)

其中前兩種鎖定讀,須要用戶本身顯式使用,最後一種是自動添加的。

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

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

 

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

  2.鎖定讀(當前讀)

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(讀取提交內容):一個事務只能看見已經提交事務所作的改變。(不可重複讀+幻讀)

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)。在這個級別,可能致使大量的超時現象和鎖競爭,主要用於分佈式事務

以下表:

mysql-innodb可能出現的問題統計表
不一樣隔離級別/可能出現的問題 髒讀 不可重複讀 幻讀
Read Uncommitted(讀取未提交內容)
Read Committed(讀取提交內容)
Repeatable Read(可重讀)

Serializable(可串行化)

 

 

 

 

 

 

 

 

 

 

 

3.2 傳播機制

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

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提供者也可能支持嵌套事務。

4、總結

本節講解了事務的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.外部不存在事務,內部建立新事務,內部異常回滾

 

 

 

 

 

 

 

 

 

=======參考========

Mysql官方文檔:官網飛機票

書籍:《Mysql技術內幕-InnoDB存儲引擎》

相關文章
相關標籤/搜索