維基百科:
數據庫事務(簡稱:事務)是數據庫管理系統執行過程當中的一個邏輯單位,由一個有限的數據庫操做序列構成。
理解:
事務(Transaction)是數據庫區別於文件系統的重要特性之一。傳統關係型數據庫設計原則是知足 ACID特性,用以保證數據庫事務的正確執行。Mysql的innoDB引擎就很好的支持了ACID。html
(箭頭後,翻譯自官網介紹:InnoDB and the ACID Model )mysql
1,原子性(Atomicity):一個事務必須被視爲一個不可分割的最小工做單元,整個事務中的全部操做要麼所有提交成功,要麼所有失敗回滾。--》主要涉及InnoDB事務。相關特性:事務的提交,回滾,信息表。spring
2,一致性(consistency):數據庫老是從一個一致性的狀態轉換到另外一個一致性的狀態。在事務開始先後,數據庫的完整性約束沒有被破壞。例如違反了惟一性,必須撤銷事務,返回初始狀態。--》主要涉及內部InnoDB處理,以保護數據不受崩潰,相關特性:雙寫緩衝、崩潰恢復。sql
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.session
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個角度來看: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鎖)
其中前兩種鎖定讀,須要用戶本身顯式使用,最後一種是自動添加的。
一致性非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎經過多版本控制(multi versionning)的方式來讀取當前執行時間數據庫中行的數據,若是讀取的行正在執行DELETE或UPDATE操做,這時讀取操做不會所以等待行上鎖的釋放。相反的,InnoDB會去讀取行的一個快照數據
上面展現了InnoDB存儲引擎一致性的非鎖定讀。之因此稱爲非鎖定讀,由於不須要等待訪問的行上X鎖的釋放。快照數據是指該行以前版本的數據,該實現是經過undo段來完成。而undo用來事務中的回滾數據,所以快照數據自己沒有額外的開銷,此外,讀取快照數據不須要上鎖,由於沒有事務須要對歷史數據進行修改操做。
三種鎖
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)。在這個級別,可能致使大量的超時現象和鎖競爭,主要用於分佈式事務。
以下表:
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企業應用開發實戰》