在執行SQL語句的時候,某些業務要求,一系列操作必須全部執行,而不能僅執行一部分。例如,一個轉賬操作:
-- 從id=1的賬戶給id=2的賬戶轉賬100元
-- 第一步:將id=1的A賬戶餘額減去100
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 第二步:將id=2的B賬戶餘額加上100
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
這兩條SQL語句必須全部執行,或者,由於某些原因,如果第一條語句成功,第二條語句失敗,就必須全部撤銷。
這種把多條語句作爲一個整體進行操作的功能,被稱爲數據庫事務。數據庫事務可以確保該事務範圍內的所有操作都可以全部成功或者全部失敗。如果事務失敗,那麼效果就和沒有執行這些SQL一樣,不會對數據庫數據有任何改動。
可見,數據庫事務具有ACID這4個特性:
- A:Atomic,原子性,將所有SQL作爲原子工作單元執行,要麼全部執行,要麼全部不執行;
- C:Consistent,一致性,事務完成後,所有數據的狀態都是一致的,即A賬戶只要減去了100,B賬戶則必定加上了100;
- I:Isolation,隔離性,如果有多個事務併發執行,每個事務作出的修改必須與其他事務隔離;
- D:Duration,持久性,即事務完成後,對數據庫數據的修改被持久化存儲。
對於單條SQL語句,數據庫系統自動將其作爲一個事務執行,這種事務被稱爲隱式事務。
要手動把多條SQL語句作爲一個事務執行,使用BEGIN
開啓一個事務,使用COMMIT
提交一個事務,這種事務被稱爲顯式事務,例如,把上述的轉賬操作作爲一個顯式事務:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
很顯然多條SQL語句要想作爲一個事務執行,就必須使用顯式事務。
COMMIT
是指提交事務,即試圖把事務內的所有SQL所做的修改永久保存。如果COMMIT
語句執行失敗了,整個事務也會失敗。
有些時候,我們希望主動讓事務失敗,這時,可以用ROLLBACK
回滾事務,整個事務會失敗:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
ROLLBACK;
隔離級別
對於兩個併發執行的事務,如果涉及到操作同一條記錄的時候,可能會發生問題。因爲併發操作會帶來數據的不一致性,包括髒讀、不可重複讀、幻讀等。數據庫系統提供了隔離級別來讓我們有針對性地選擇事務的隔離級別,避免數據不一致的問題。
SQL標準定義了4種隔離級別,分別對應可能出現的數據不一致的情況:
Isolation Level | 髒讀(Dirty Read) | 不可重複讀(Non Repeatable Read) | 幻讀(Phantom Read) |
---|---|---|---|
Read Uncommitted | Yes | Yes | Yes |
Read Committed | - | Yes | Yes |
Repeatable Read | - | - | Yes |
Serializable | - | - | - |
Read Uncommitted
Read Uncommitted是隔離級別最低的一種事務級別。在這種隔離級別下,一個事務會讀到另一個事務更新後但未提交的數據,如果另一個事務回滾,那麼當前事務讀到的數據就是髒數據,這就是髒讀(Dirty Read)。
Read Committed
在Read Committed隔離級別下,一個事務可能會遇到不可重複讀(Non Repeatable Read)的問題。
不可重複讀是指,在一個事務內,多次讀同一數據,在這個事務還沒有結束時,如果另一個事務恰好修改了這個數據,那麼,在第一個事務中,兩次讀取的數據就可能不一致。
Repeatable Read
在Repeatable Read隔離級別下,一個事務可能會遇到幻讀(Phantom Read)的問題。
幻讀是指,在一個事務中,第一次查詢某條記錄,發現沒有,但是,當試圖更新這條不存在的記錄時,竟然能成功,並且,再次讀取同一條記錄,它就神奇地出現了。
Serializable
Serializable是最嚴格的隔離級別。在Serializable隔離級別下,所有事務按照次序依次執行,因此,髒讀、不可重複讀、幻讀都不會出現。
雖然Serializable隔離級別下的事務具有最高的安全性,但是,由於事務是串行執行,所以效率會大大下降,應用程序的性能會急劇降低。如果沒有特別重要的情景,一般都不會使用Serializable隔離級別。
默認隔離級別
如果沒有指定隔離級別,數據庫就會使用默認的隔離級別。在MySQL中,如果使用InnoDB,默認的隔離級別是Repeatable Read。