spring事務詳解(二)簡單樣例mysql
spring事務詳解(三)源碼詳解spring
不少coder在不理解事務的原理甚至連基本概念都不清楚的狀況下,就去使用數據庫事務,是極容易出錯,寫出一些本身不能掌控的代碼。網上不少文章要不就是概念,或者一點源碼,或者一點測試驗證,都不足以全面瞭解事務,因此本文出現了,本系列Spring事務詳解包含四部分:緩存
第一章 講概念,對事務的總體有一個瞭解。session
第二章 簡單樣例,瞭解如何用。併發
第三章 從源碼來看底層實現機制。數據庫設計
第四章 實例測試驗證。分佈式
第五章 總結提升。
我的能力有限,有任何不當之處,麻煩指出。
全文基於Mysql innodb引擎。Mysql官方文檔:官網飛機票 ,推薦書籍:《Mysql技術內幕-InnoDB存儲引擎》。
spring事務領頭人叫Juergen Hoeller,于爾根·糊了...先混個臉熟哈,他寫了幾乎所有的spring事務代碼。讀源碼先拜神,掌握他的源碼的風格,讀起來會通暢不少。最後一節我們總結下這個大神的代碼風格。
事務(Transaction)是數據庫區別於文件系統的重要特性之一。目前國際承認的數據庫設計原則是ACID特性,用以保證數據庫事務的正確執行。Mysql的innodb引擎中的事務就徹底符合ACID特性。
spring對於事務的支持,分層概覽圖以下:
(箭頭後,翻譯自官網介紹:InnoDB and the ACID Model )
原子性(Atomicity):一個事務必須被視爲一個不可分割的最小工做單元,整個事務中的全部操做要麼所有提交成功,要麼所有失敗回滾。--》主要涉及InnoDB事務。相關特性:事務的提交,回滾,信息表。
一致性(consistency):數據庫老是從一個一致性的狀態轉換到另外一個一致性的狀態。在事務開始先後,數據庫的完整性約束沒有被破壞。例如違反了惟一性,必須撤銷事務,返回初始狀態。--》主要涉及內部InnoDB處理,以保護數據不受崩潰,相關特性:雙寫緩衝、崩潰恢復。
隔離性(isolation):每一個讀寫事務的對象對其餘事務的操做對象能相互分離,即:事務提交前對其餘事務是不可見的,一般內部加鎖實現。--》主要涉及事務,尤爲是事務隔離級別,相關特性:隔離級別、innodb鎖的底層實現細節。
持久性(durability):一旦事務提交,則其所作的修改會永久保存到數據庫。--》涉及到MySQL軟件特性與特定硬件配置的相互影響,相關特性:4個配置項:雙寫緩衝開關、事務提交刷新log的級別、binlog同步頻率、表文件;寫緩存、操做系統對於fsync()的支持、
備份策略等。
要保證事務的ACID特性,spring給事務定義了6個屬性,對應於聲明式事務註解(org.springframework.transaction.annotation.Transactional)@Transactional(key1=*,key2=*...)
其中隔離級別和傳播機制比較複雜,我們細細地品一品。
這一塊比較複雜,咱們從3個角度來看:3種錯誤現象、mysql的底層技術支持、分級處理策略。這一小節必定要好好看,已經開始涉及核心原理了。
髒讀(Drity Read):事務A更新記錄但未提交,事務B查詢出A未提交記錄。
不可重複讀(Non-repeatable read): 事務A讀取一次,此時事務B對數據進行了更新或刪除操做,事務A再次查詢數據不一致。
幻讀(Phantom Read): 事務A讀取一次,此時事務B插入一條數據事務A再次查詢,記錄多了。
官網飛機票:InnoDB Transaction Model
在MVCC中,讀操做能夠分紅兩類,快照讀(Snapshot read)和當前讀(current read)。
快照讀:普通的select
當前讀:
其中前兩種鎖定讀,須要用戶本身顯式使用,最後一種是自動添加的。
一致性非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎經過多版本控制(multi versionning)的方式來讀取當前執行時間數據庫中行的數據,若是讀取的行正在執行DELETE或UPDATE操做,這是讀取操做不會所以等待行上鎖的釋放。相反的,InnoDB會去讀取行的一個快照數據
上面展現了InnoDB存儲引擎一致性的非鎖定讀。之因此稱爲非鎖定讀,由於不須要等待訪問的行上X鎖的釋放。快照數據是指該行以前版本的數據,該實現是經過undo段來完成。而undo用來事務中的回滾數據,所以快照數據自己沒有額外的開銷,此外,讀取快照數據不須要上鎖,由於沒有事務須要對歷史數據進行修改操做。
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當即提交)
官網描述:
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)。在這個級別,可能致使大量的超時現象和鎖競爭,主要用於分佈式事務。
以下表:
不一樣隔離級別/可能出現的問題 | 髒讀 | 不可重複讀 | 幻讀 |
Read Uncommitted(讀取未提交內容) | ✅ | ✅ | ✅ |
Read Committed(讀取提交內容) | ❎ | ✅ | ✅ |
Repeatable Read(可重讀) | ❎ | ❎ | ✅ |
Serializable(可串行化) | ❎ | ❎ | ❎ |
org.springframework.transaction包下有一個事務定義接口TransactionDefinition,定義了7種事務傳播機制,不少人對傳播機制的曲解從概念開始,因此特意翻譯了一下源碼註釋以下:
支持當前事務;若是不存在,建立一個新的。相似於同名的EJB事務屬性。這一般是事務定義的默認設置,一般定義事務同步做用域。
支持當前事務;若是不存在事務,則以非事務方式執行。相似於同名的EJB事務屬性。
注意:
對於具備事務同步的事務管理器,PROPAGATION_SUPPORTS與沒有事務稍有不一樣,由於它可能在事務範圍內定義了同步。所以,相同的資源(JDBC的Connection、Hibernate的Session等)將在整個指定範圍內共享。注意,確切的行爲取決於事務管理器的實際同步配置!
當心使用PROPAGATION_SUPPORTS!特別是,不要依賴PROPAGATION_REQUIRED或PROPAGATION_REQUIRES_NEW,在PROPAGATION_SUPPORTS範圍內(這可能致使運行時的同步衝突)。若是這種嵌套不可避免,請確保適當地配置事務管理器(一般切換到「實際事務上的同步」)。
支持當前事務;若是當前事務不存在,拋出異常。相似於同名的EJB事務屬性。
注意:
PROPAGATION_MANDATORY範圍內的事務同步老是由周圍的事務驅動。
建立一個新事務,若是存在當前事務,則掛起當前事務。相似於同名的EJB事務屬性。
注意:實際事務掛起不會在全部事務管理器上開箱即用。這一點特別適用於JtaTransactionManager,它須要TransactionManager的支持。
PROPAGATION_REQUIRES_NEW範圍老是定義本身的事務同步。現有同步將被掛起並適當地恢復。
不支持當前事務,存在事務掛起當前事務;始終以非事務方式執行。相似於同名的EJB事務屬性。
注意:實際事務掛起不會在全部事務管理器上開箱即用。這一點特別適用於JtaTransactionManager,它須要TransactionManager的支持。
事務同步在PROPAGATION_NOT_SUPPORTED範圍內是不可用的。現有同步將被掛起並適當地恢復。
不支持當前事務;若是當前事務存在,拋出異常。相似於同名的EJB事務屬性。
注意:事務同步在PROPAGATION_NEVER範圍內不可用。
若是當前事務存在,則在嵌套事務中執行,若是當前沒有事務,相似PROPAGATION_REQUIRED(建立一個新的)。EJB中沒有相似的功能。
注意:實際建立嵌套事務只對特定的事務管理器有效。開箱即用,這只適用於 DataSourceTransactionManager(JDBC 3.0驅動)。一些JTA提供者也可能支持嵌套事務。
本節講解了事務的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存儲引擎》