- 學習重點
- 事務是需要在同一個處理單元中執行的一系列更新處理的集合。通過使用事務,可以對數據庫中的數據更新處理的提交和取消進行管理。
- 事務處理的終止指令包括
COMMIT
(提交處理)和ROLLBACK
(取消處理)兩種。 - DBMS 的事務具有原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability)四種特性。通常將這四種特性的首字母結合起來,統稱為 ACID 特性。
一、什么是事務
估計有些讀者對事務(transaction)這個詞並不熟悉,它通常被用於商務貿易或者經濟活動中,但是在 RDBMS 中,事務是對表中數據進行更新的單位。簡單來講,事務就是需要在同一個處理單元中執行的一系列更新處理的集合。
KEYWORD
- 事務
如前幾篇所述,對表進行更新需要使用 INSERT
、DELETE
或者 UPDATE
三種語句。但通常情況下,更新處理並不是執行一次就結束了,而是需要執行一系列連續的操作。這時,事務就能體現出它的價值了。
說到事務的例子,請大家思考一下下述情況。
現在,請大家把自己想象為管理 Product
(商品)表的程序員或者軟件工程師。銷售部門的領導對你提出了如下要求。
“某某,經會議討論,我們決定把運動 T 恤的銷售單價下調 1000 日元,同時把 T 恤衫的銷售單價上浮 1000 日元,麻煩你去更新一下數據庫。”
由於大家已經學習了更新數據的方法 —— 只需要使用 UPDATE
進行更新就可以了,所以肯定會直接回答“知道了,請您放心吧”。
此時的事務由如下兩條更新處理所組成。
-
更新商品信息的事務
① 將運動T恤的銷售單價降低 1000 日元
UPDATE Product SET sale_price = sale_price - 1000 WHERE product_name = '運動T恤';
② 將T恤衫的銷售單價上浮 1000 日元
UPDATE Product SET sale_price = sale_price + 1000 WHERE product_name = 'T恤衫';
上述 ① 和 ② 的操作一定要作為同一個處理單元執行。如果只執行了 ① 的操作而忘記了執行 ② 的操作,或者反過來只執行了 ② 的操作而忘記了執行 ① 的操作,一定會受到領導的嚴厲批評。遇到這種需要在同一個處理單元中執行一系列更新操作的情況,一定要使用事務來進行處理。
法則 7
事務是需要在同一個處理單元中執行的一系列更新處理的集合。
一個事務中包含多少個更新處理或者包含哪些處理,在 DBMS 中並沒有固定的標准,而是根據用戶的要求決定的(例如,運動 T 恤和 T 恤衫的銷售單價需要同時更新這樣的要求,DBMS 是無法了解的)。
二、創建事務
如果想在 DBMS 中創建事務,可以按照如下語法結構編寫 SQL 語句。
語法 6 事務的語法
事務開始語句;
DML語句①;
DML語句②;
DML語句③;
.
.
.
事務結束語句(COMMIT或者ROLLBACK);
使用事務開始語句和事務結束語句,將一系列 DML 語句(INSERT
/UPDATE
/DELETE
語句)括起來,就實現了一個事務處理。
這時需要特別注意的是事務的開始語句 [1]。實際上,在標准 SQL 中並沒有定義事務的開始語句,而是由各個 DBMS 自己來定義的。比較有代表性的語法如下所示。
-
SQL Server、PostgreSQL
BEGIN TRANSACTION
-
MySQL
START TRANSACTION
-
Oracle、DB2
無
KEYWORD
BEGIN TRANSACTION
START TRANSACTION
例如使用之前的那兩個 UPDATE
(① 和 ②)創建出的事務如代碼清單 21 所示。
代碼清單 21 更新商品信息的事務
SQL Server PostgreSQL
BEGIN TRANSACTION;
-- 將運動T恤的銷售單價降低1000日元
UPDATE Product
SET sale_price = sale_price - 1000
WHERE product_name = '運動T恤';
-- 將T恤衫的銷售單價上浮1000日元
UPDATE Product
SET sale_price = sale_price + 1000
WHERE product_name = 'T恤衫';
COMMIT;
MySQL
START TRANSACTION;
-- 將運動T恤的銷售單價降低1000日元
UPDATE Product
SET sale_price = sale_price - 1000
WHERE product_name = '運動T恤';
-- 將T恤衫的銷售單價上浮1000日元
UPDATE Product
SET sale_price = sale_price + 1000
WHERE product_name = 'T恤衫';
COMMIT;
Oracle DB2
-- 將運動T恤的銷售單價降低1000日元
UPDATE Product
SET sale_price = sale_price - 1000
WHERE product_name = '運動T恤';
-- 將T恤衫的銷售單價上浮1000日元
UPDATE Product
SET sale_price = sale_price + 1000
WHERE product_name = 'T恤衫';
COMMIT;
如上所示,各個 DBMS 事務的開始語句都不盡相同,其中 Oracle 和 DB2 並沒有定義特定的開始語句。可能大家覺得這樣的設計很巧妙,其實是因為標准 SQL 中規定了一種悄悄開始事務處理 [2] 的方法。因此,即使是經驗豐富的工程師也經常會忽略事務處理開始的時間點。大家可以試着通過詢問“是否知道某個 DBMS 中事務是什么時候開始的”,來測試學校或者公司前輩的數據庫知識。
反之,事務的結束需要用戶明確地給出指示。結束事務的指令有如下兩種。
-
COMMIT
——提交處理COMMIT
是提交事務包含的全部更新處理的結束指令(圖 3),相當於文件處理中的覆蓋保存。一旦提交,就無法恢復到事務開始前的狀態了。因此,在提交之前一定要確認是否真的需要進行這些更新。KEYWORD
-
COMMIT
-
提交
圖 3 COMMIT 的流程 = 直線進行
萬一由於誤操作提交了包含錯誤更新的事務,就只能回到重新建表、重新插入數據這樣繁瑣的老路上了。由於可能會造成數據無法恢復的后果,請大家一定要注意(特別是在執行
DELETE
語句的COMMIT
時尤其要小心)。法則 8
雖然我們可以不清楚事務開始的時間點,但是在事務結束時一定要仔細進行確認。
-
-
ROLLBACK
——取消處理ROLLBACK
是取消事務包含的全部更新處理的結束指令(圖 4),相當於文件處理中的放棄保存。一旦回滾,數據庫就會恢復到事務開始之前的狀態(代碼清單 22)。通常回滾並不會像提交那樣造成大規模的數據損失。KEYWORD
-
ROLLBACK
-
回滾
圖 4 ROLLBACK 的流程 = 掉頭回到起點
代碼清單 22 事務回滾的例子
SQL Server PostgreSQL
BEGIN TRANSACTION; ------------------- ① -- 將運動T恤的銷售單價降低1000日元 UPDATE Product SET sale_price = sale_price - 1000 WHERE product_name = '運動T恤'; -- 將T恤衫的銷售單價上浮1000日元 UPDATE Product SET sale_price = sale_price + 1000 WHERE product_name = 'T恤衫'; ROLLBACK;
特定的 SQL
至此,我們已經知道各個 DBMS 中關於事務的語法不盡相同。代碼清單 22 中的語句在 MySQL 中執行時需要將 ① 語句改寫為“
START TRANSACTION
”,而在 Oracle 和 DB2 中執行時則無需 ① 語句(請將其刪除),具體請參考 創建事務。上述事務處理執行之后,表中的數據不會發生任何改變。這是因為執行最后一行的
ROLLBACK
之后,所有的處理都被取消了。因此,回滾執行起來就無需像提交時那樣小心翼翼了(即使是想要提交的情況,也只需要重新執行事務處理就可以了)。 -
專欄
事務處理何時開始
之前我們說過,事務並沒有標准的開始指令存在,而是根據 DBMS 的不同而不同。
實際上,幾乎所有的數據庫產品的事務都無需開始指令。這是因為大部分情況下,事務在數據庫連接建立時就已經悄悄開始了,並不需要用戶再明確發出開始指令。例如,使用 Oracle 時,數據庫連接建立之后,第一條 SQL 語句執行的同時,事務就已經悄悄開始了。
像這樣不使用指令而悄悄開始事務的情況下,應該如何區分各個事務呢?通常會有如下兩種情況。
A: 每條 SQL 語句就是一個事務(自動提交模式)
B: 直到用戶執行
COMMIT
或者ROLLBACK
為止算作一個事務KEYWORD
- 自動提交模式
通常的 DBMS 都可以選擇其中任意一種模式。默認使用自動提交模式的 DBMS 有 SQL Server、PostgreSQL 和 MySQL 等 [3] DML 語句如下所示,每一條語句都括在事務的開始語句和結束語句之中。
BEGIN TRANSACTION; -- 將運動T恤的銷售單價降低1000日元 UPDATE Product SET sale_price = sale_price - 1000 WHERE product_name = '運動T恤'; COMMIT; BEGIN TRANSACTION; -- 將T恤衫的銷售單價上浮1000日元 UPDATE Product SET sale_price = sale_price + 1000 WHERE product_name = 'T恤衫'; COMMIT;
在默認使用 B 模式的 Oracle 中,事務都是直到用戶自己執行提交或者回滾指令才會結束。
自動提交的情況需要特別注意的是
DELETE
語句。如果不是自動提交,即使使用DELETE
語句刪除了數據表,也可以通過ROLLBACK
命令取消該事務的處理,恢復表中的數據。但這僅限於明示開始事務,或者關閉自動提交的情況。如果不小心在自動提交模式下執行了DELETE
操作,即使再回滾也無濟於事了。這是一個很嚴重的問題,初學者難免會碰到這樣的麻煩。一旦誤刪了數據,如果無法重新插入,是不是想哭的心都有了?所以一定要特別小心。
三、ACID 特性
DBMS 的事務都遵循四種特性,將這四種特性的首字母結合起來統稱為 ACID 特性。這是所有 DBMS 都必須遵守的規則。
KEYWORD
- ACID 特性
-
原子性(Atomicity)
KEYWORD
- 原子性(Atomicity)
原子性是指在事務結束時,其中所包含的更新處理要么全部執行,要么完全不執行,也就是要么占有一切要么一無所有。例如,在之前的例子中,在事務結束時,絕對不可能出現運動 T 恤的價格下降了,而 T 恤衫的價格卻沒有上漲的情況。該事務的結束狀態,要么是兩者都執行了(
COMMIT
),要么是兩者都未執行(ROLLBACK
)。從事務中途停止的角度去考慮,就能比較容易理解原子性的重要性了。由於用戶在一個事務中定義了兩條
UPDATE
語句,DBMS 肯定不會只執行其中一條,否則就會對業務處理造成影響。 -
一致性(Consistency)
一致性指的是事務中包含的處理要滿足數據庫提前設置的約束,如主鍵約束或者
NOT NULL
約束等。例如,設置了NOT NULL
約束的列是不能更新為NULL
的,試圖插入違反主鍵約束的記錄就會出錯,無法執行。對事務來說,這些不合法的 SQL 會被回滾。也就是說,這些 SQL 處理會被取消,不會執行。一致性也稱為完整性(圖 5)。
KEYWORD
-
一致性(Consistency)
-
完整性
圖 5 保持完整性的流程
-
-
隔離性(Isolation)
隔離性指的是保證不同事務之間互不干擾的特性。該特性保證了事務之間不會互相嵌套。此外,在某個事務中進行的更改,在該事務結束之前,對其他事務而言是不可見的。因此,即使某個事務向表中添加了記錄,在沒有提交之前,其他事務也是看不到新添加的記錄的。
KEYWORD
- 隔離性(Isolation)
-
持久性(Durability)
持久性也可以稱為耐久性,指的是在事務(不論是提交還是回滾)結束后,DBMS 能夠保證該時間點的數據狀態會被保存的特性。即使由於系統故障導致數據丟失,數據庫也一定能通過某種手段進行恢復。
如果不能保證持久性,即使是正常提交結束的事務,一旦發生了系統故障,也會導致數據丟失,一切都需要從頭再來。
保證持久性的方法根據實現的不同而不同,其中最常見的就是將事務的執行記錄保存到硬盤等存儲介質中(該執行記錄稱為日志)。當發生故障時,可以通過日志恢復到故障發生前的狀態。
KEYWORD
-
持久性(Durability)
-
日志
-
請參閱
(完)