事務是由一步或幾步數據庫操作序列組成邏輯執行單元,這系列操作要麼全部執行,要麼全部放棄執行。程序和事務是兩個不同的概念。一般而言:一段程序中可能包含多個事務。
事務具有四個特性:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持續性(Durability)。這四個特性也簡稱ACID性。
1)原子性:事務是應用中最小的執行單位,就如原子是自然界最小顆粒,具有不可再分的特征一樣。事務是應用中不可再分的最小邏輯執行體。
2)一致性:事務執行的結果,必須使數據庫從一個一致性狀態,變到另一個一致性狀態。當數據庫中只包含事務成功提交的結果時,數據庫處於一致性狀態。一致性是通過原子性來保證的。
3)隔離性:各個事務的執行互不干擾,任意一個事務的內部操作對其他並發的事務,都是隔離的。也就是說:並發執行的事務之間不能看到對方的中間狀態,並發執行的事務之間不能相互影響。
4)持續性:持續性也稱為持久性,指事務一旦提交,對數據所做的任何改變,都要記錄到永久存儲器中,通常是保存進物理數據庫。
在關系型數據庫中,事務的隔離性分為四個隔離級別,在解讀這四個級別前先介紹幾個關於讀數據的概念。
1)髒讀(Dirty Reads):所謂髒讀就是對髒數據(Drity Data)的讀取,而髒數據所指的就是未提交的數據。也就是說,一個事務正在對一條記錄做修改,在這個事務完成並提交之前,這條數據是處於待定狀態的(可能提交也可能回滾),這時,第二個事務來讀取這條沒有提交的數據,並據此做進一步的處理,就會產生未提交的數據依賴關系。這種現象被稱為髒讀。
2)不可重復讀(Non-Repeatable Reads):一個事務先後讀取同一條記錄,但兩次讀取的數據不同,我們稱之為不可重復讀。也就是說,這個事務在兩次讀取之間該數據被其它事務所修改。
3)幻讀(Phantom Reads):一個事務按相同的查詢條件重新讀取以前檢索過的數據,卻發現其他事務插入了滿足其查詢條件的新數據,這種現象就稱為幻讀。
事務四個隔離級別對比:
1)未提交讀(Read Uncommitted):SELECT語句以非鎖定方式被執行,所以有可能讀到髒數據,隔離級別最低。
2)提交讀(Read Committed):只能讀取到已經提交的數據。即解決了髒讀,但未解決不可重復讀。
3)可重復讀(Repeated Read):在同一個事務內的查詢都是事務開始時刻一致的,InnoDB的默認級別。在SQL標准中,該隔離級別消除了不可重復讀,但是還存在幻讀。
4)串行讀(Serializable):完全的串行化讀,所有SELECT語句都被隱式的轉換成SELECT ... LOCK IN SHARE MODE,即讀取使用表級共享鎖,讀寫相互都會阻塞。隔離級別最高。
隔離級別對比表:
數據庫的事務有下列語句組成:
一組DML(Data Manipulate Language,即數據操作語言)經過這組DML修改後數據將保持較好的一致性。
一個DDL(Data Definition Language,即數據定義語言)語句。
一個DCL(Data control Language,即數據控制語言)語句。
DDL和DCL語句最多只能有一個,因為DDL和DCL語句都會導致事務立即提交。
當事務所包含的全部數據庫操作都成功執行後,應該提交(commit)事務,使這些修改永久生效。
事務提交有兩種方式:顯式提交和自動提交。
(1)顯式提交:使用commit。
(2)自動提交:執行DDL或DCL,或者程序正常退出。
數據庫事務傳播級別,指的是事務嵌套時,應該采用什麼策略,即在一個事務中調用別的事務,該怎麼辦
假如有一下兩個事務:
ServiceA { void methodA () { ServiceB . methodB (); } } ServiceB { void methodB () { } }
1 : PROPAGATION_REQUIRED
加入當前正要執行的事務不在另外一個事務裡,那麼就起一個新的事務
比如說, ServiceB.methodB 的事務級別定義為 PROPAGATION_REQUIRED, 那麼由於執行 ServiceA.methodA 的時候,
ServiceA.methodA 已經起了事務,這時調用 ServiceB.methodB , ServiceB.methodB 看到自己已經運行在 ServiceA.methodA
的事務內部,就不再起新的事務。而假如 ServiceA.methodA 運行的時候發現自己沒有在事務中,他就會為自己分配一個事務。
這樣,在 ServiceA.methodA 或者在 ServiceB.methodB 內的任何地方出現異常,事務都會被回滾。即使 ServiceB.methodB 的事務已經被
提交,但是 ServiceA.methodA 在接下來 fail 要回滾, ServiceB.methodB 也要回滾
2 : PROPAGATION_SUPPORTS
如果當前在事務中,即以事務的形式運行,如果當前不再一個事務中,那麼就以非事務的形式運行
3 : PROPAGATION_MANDATORY
必須在一個事務中運行。也就是說,他只能被一個父事務調用。否則,他就要拋出異常
4 : PROPAGATION_REQUIRES_NEW
這個就比較繞口了。 比如我們設計 ServiceA.methodA 的事務級別為 PROPAGATION_REQUIRED , ServiceB.methodB 的事務級別為 PROPAGATION_REQUIRES_NEW ,
那麼當執行到 ServiceB.methodB 的時候, ServiceA.methodA 所在的事務就會掛起, ServiceB.methodB 會起一個新的事務,等待 ServiceB.methodB 的事務完成以後,
他才繼續執行。他與 PROPAGATION_REQUIRED 的事務區別在於事務的回滾程度了。因為 ServiceB.methodB 是新起一個事務,那麼就是存在
兩個不同的事務。如果 ServiceB.methodB 已經提交,那麼 ServiceA.methodA 失敗回滾, ServiceB.methodB 是不會回滾的。如果 ServiceB.methodB 失敗回滾,
如果他拋出的異常被 ServiceA.methodA 捕獲, ServiceA.methodA 事務仍然可能提交。
5 : PROPAGATION_NOT_SUPPORTED
當前不支持事務。比如 ServiceA.methodA 的事務級別是 PROPAGATION_REQUIRED ,而 ServiceB.methodB 的事務級別是 PROPAGATION_NOT_SUPPORTED ,
那麼當執行到 ServiceB.methodB 時, ServiceA.methodA 的事務掛起,而他以非事務的狀態運行完,再繼續 ServiceA.methodA 的事務。
6 : PROPAGATION_NEVER
不能在事務中運行。假設 ServiceA.methodA 的事務級別是 PROPAGATION_REQUIRED , 而 ServiceB.methodB 的事務級別是 PROPAGATION_NEVER ,
那麼 ServiceB.methodB 就要拋出異常了。
7 : PROPAGATION_NESTED
理解 Nested 的關鍵是 savepoint 。他與 PROPAGATION_REQUIRES_NEW 的區別是, PROPAGATION_REQUIRES_NEW 另起一個事務,將會與他的父事務相互獨立,
而 Nested 的事務和他的父事務是相依的,他的提交是要等和他的父事務一塊提交的。也就是說,如果父事務最後回滾,他也要回滾的。
而 Nested 事務的好處是他有一個 savepoint 。