MySQL的事務和隔離級別

原文首發自個人博客:http://www.toxingwang.com/database/mysql/1454.html

一、什麼是事務?

數據庫的事物,是指將一系列的操作作爲一個邏輯單元來執行,即加入由十條SQL語句組成的一個事物,則要麼則十條都執行成功,要麼都不執行!事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向數據的資源。一個邏輯工作單元要成爲事務,必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性。

爲什麼要用事務?

假想一下,沒有事務的情況會發生什麼情況:你通過網銀向別人轉的賬戶轉錢,結果執行到一半,出現故障(如服務器宕機),你的錢被減少了,而你轉賬的對方賬戶錢卻沒有加上去!錢消失了!

如果有事務會是怎麼樣的?當執行到一半出錯,那已經執行的也會被撤銷,保障ACID!

二、事務的ACID屬性:

原子性(atomicity)

事務必須是原子工作單元;對於其數據修改,要麼全都執行,要麼全都不執行。通常,與某個事務關聯的操作具有共同的目標,並且是相互依賴的。如果系統只執行這些操作的一個子集,則可能會破壞事務的總體目標。原子性消除了系統處理操作子集的可能性。

一致性(consistency)

事務在完成時,必須使所有的數據都保持一致狀態。在相關數據庫中,所有規則都必須應用於事務的修改,以保持所有數據的完整性。事務結束時,所有的內部數據結構(如 B 樹索引或雙向鏈表)都必須是正確的。某些維護一致性的責任由應用程序開發人員承擔,他們必須確保應用程序已強制所有已知的完整性約束。例如,當開發用於轉帳的應用程序時,應避免在轉帳過程中任意移動小數點。

隔離性(isolation)

由併發事務所作的修改必須與任何其它併發事務所作的修改隔離。事務查看數據時數據所處的狀態,要麼是另一併發事務修改它之前的狀態,要麼是另一事務修改它之後的狀態,事務不會查看中間狀態的數據。這稱爲隔離性,因爲它能夠重新裝載起始數據,並且重播一系列事務,以使數據結束時的狀態與原始事務執行的狀態相同。當事務可序列化時將獲得最高的隔離級別。在此級別上,從一組可並行執行的事務獲得的結果與通過連續運行每個事務所獲得的結果相同。由於高度隔離會限制可並行執行的事務數,所以一些應用程序降低隔離級別以換取更大的吞吐量。

持久性(durability)

事務完成之後,它對於系統的影響是永久性的。該修改即使出現致命的系統故障也將一直保持。保證事務的持久性,主要通過如下方式完成:

  • 事務提交之前就已經寫入數據至持久性存儲;

  • 通過事務日誌協助完成(事務日誌使用連續IO,因此寫入非常快)

三、MySQL對事務的支持情況:

MySQL數據庫中,只有使用InnoDB 和 BDB存儲引擎才支持事務,而其他存儲引擎(如常用的MyISAM引擎)是不支持事務的,這點特別需要注意。

四、事務的隔離級別詳解

SQL標準定義了4類隔離級別,包括了一些具體規則,用來限定事務內外的哪些改變是可見的,哪些是不可見的。低級別的隔離級一般支持更高的併發處理,並擁有更低的系統開銷。

Read Uncommitted(讀取未提交內容)

在該隔離級別,所有事務都可以看到其他未提交事務的執行結果。本隔離級別很少用於實際應用,因爲它的性能也不比其他級別好多少。讀取未提交的數據,也被稱之爲髒讀(Dirty Read)。

Read Committed(讀取提交內容)

這是大多數數據庫系統的默認隔離級別(但不是MySQL默認的)。它滿足了隔離的簡單定義:一個事務只能看見已經提交事務所做的改變。這種隔離級別 也支持所謂的不可重複讀(Nonrepeatable Read),因爲同一事務的其他實例在該實例處理其間可能會有新的commit,所以同一select可能返回不同結果。

Repeatable Read(可重讀)

這是MySQL的默認事務隔離級別,它確保同一事務的多個實例在併發讀取數據時,會看到同樣的數據行。不過理論上,這會導致另一個棘手的問題:幻讀 (Phantom Read)。簡單的說,幻讀指當用戶讀取某一範圍的數據行時,另一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的數據行時,會發現有新的“幻影” 行。InnoDB和Falcon存儲引擎通過多版本併發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。

Serializable(可串行化)

這是最高的隔離級別,它通過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。簡言之,它是在每個讀的數據行上加上共享鎖。在這個級別,可能導致大量的超時現象和鎖競爭。

這四種隔離級別採取不同的鎖類型來實現,若讀取的是同一個數據的話,就容易發生問題。例如:

髒讀(Drity Read):某個事務已更新一份數據,另一個事務在此時讀取了同一份數據,由於某些原因,前一個RollBack了操作,則後一個事務所讀取的數據就會是不正確的。

不可重複讀(Non-repeatable read):在一個事務的兩次查詢之中數據不一致,這可能是兩次查詢過程中間插入了一個事務更新的原有的數據。

幻讀(Phantom Read):在一個事務的兩次查詢中數據筆數不一致,例如有一個事務查詢了幾列(Row)數據,而另一個事務卻在此時插入了新的幾列數據,先前的事務在接下來的查詢中,就會發現有幾列數據是它先前所沒有的。

在MySQL中,實現了這四種隔離級別,分別有可能產生問題如下所示:

圖片

下面,將利用MySQL的客戶端程序,分別測試幾種隔離級別。測試數據庫爲test,表爲tx;表結構:

id int

num int

兩個命令行客戶端分別爲A,B;不斷改變A的隔離級別,在B端修改數據。

(一)、將A的隔離級別設置爲read uncommitted(未提交讀)

在B未更新數據之前:

客戶端A:

圖片

B更新數據:

客戶端B:

圖片

客戶端A:

圖片

經過上面的實驗可以得出結論,事務B更新了一條記錄,但是沒有提交,此時事務A可以查詢出未提交記錄。造成髒讀現象。未提交讀是最低的隔離級別。

(二)、將客戶端A的事務隔離級別設置爲read committed(已提交讀)

在B未更新數據之前:

客戶端A:

圖片

5.2 事務的其他知識點:

事務併發執行的好處:提高吞吐量和資源利用率、減少等待時間。

事務調度的分類:可恢復調度、無級聯調度。

併發控制依賴的技術手段:鎖、時間戳、多版本和快照隔離

鎖:讀鎖(共享鎖)、寫鎖(獨佔鎖、排查鎖)

鎖粒度:從大到小,Mysql服務器只支持表級鎖,行鎖需要有存儲引擎完成;


5.3 事務的控制命令:

START TANSACTION:啓動
SQL語句
……

ROLLBACK: 回滾
COMMIT: 提交

事務流程如下:

<a href="http://www.toxingwang.com/wp-content/uploads/2013/09/mysql.jpg" class="cboxElement" rel="example4" 1454"="" style="text-decoration: none; color: rgb(1, 150, 227);">圖片

需要注意的是,事務的回滾需要在提交之前,如果已經提交了,就無法回滾了。

另外在Mysql下,如果沒有明確啓動事務,且autocommit變量設置爲1,就會實現自動提交,每一個操作都直接提交

查看當前autocommit設置:

mysql> SELECT @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.01 sec)

在MySQL的InnoDB存儲引擎下,爲了保障數據的持久性,默認將autocommit置爲1,以實現自動提交事務。但爲了數據的安全性,建議明確使用事務


5.4 事務的保存點:

在一個事務中,如果包含的語句較多,如一個事務要執行100條SQL語句,如果執行到99條的時候,發現95條出現了錯誤,需要回退,莫非只能全部回滾?

爲了防止這樣的情況發生,引入了保存點(SAVEPOINT)的概念。加入還是上述的100條語句,如果每十條語句建立一個保存點,那麼則只需回滾至第90條即可,極大的提高了效率。

具體命令和流程如下:

START TANSACTION:啓動
SQL
……

SAVEPOINT sid_one :創建保存點
SQL

……

SAVEPOINT sid_two :創建保存點

……

ROLLBACK TO sid :回滾到指定保存點
COMMIT: 提交

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章