本文給大家介紹資料庫中用來管理數據更新的重要概念——SQL 事務。簡單來講,事務就是需要在同一個處理單元中執行的一系列更新處理的集合。 本文重點 事務是需要在同一個處理單元中執行的一系列更新處理的集合。通過使用事務,可以對資料庫中的數據更新處理的提交和取消進行管理。 事務處理的終止指令包括 COMM ...
目錄
本文給大家介紹資料庫中用來管理數據更新的重要概念——SQL 事務。簡單來講,事務就是需要在同一個處理單元中執行的一系列更新處理的集合。
本文重點
事務是需要在同一個處理單元中執行的一系列更新處理的集合。通過使用事務,可以對資料庫中的數據更新處理的提交和取消進行管理。
事務處理的終止指令包括
COMMIT
(提交處理)和ROLLBACK
(取消處理)兩種。DBMS 的事務具有原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability)四種特性。通常將這四種特性的首字母結合起來,統稱為 ACID 特性。
一、什麼是事務
估計有些讀者對事務(transaction)這個詞並不熟悉,它通常被用於商務貿易或者經濟活動中,但是在 RDBMS 中,事務是對錶中數據進行更新的單位。簡單來講,事務就是需要在同一個處理單元中執行的一系列更新處理的集合。
如 SQL 如何插入、刪除和更新數據 所述,對錶進行更新需要使用 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
無
例如使用之前的那兩個 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),相當於文件處理中的覆蓋保存。一旦提交,就無法恢復到事務開始前的狀態了。因此,在提交之前一定要確認是否真的需要進行這些更新。
萬一由於誤操作提交了包含錯誤更新的事務,就只能回到重新建表、重新插入數據這樣繁瑣的老路上了。
由於可能會造成數據無法恢復的後果,請大家一定要註意(特別是在執行
DELETE
語句的COMMIT
時尤其要小心)。法則 8
雖然我們可以不清楚事務開始的時間點,但是在事務結束時一定要仔細進行確認。
-
ROLLBACK
——取消處理ROLLBACK
是取消事務包含的全部更新處理的結束指令(圖 4),相當於文件處理中的放棄保存。一旦回滾,資料庫就會恢復到事務開始之前的狀態(代碼清單 22)。通常回滾並不會像提交那樣造成大規模的數據損失。
代碼清單 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
為止算作一個事務通常的 DBMS 都可以選擇其中任意一種模式。預設使用自動提交模式的 DBMS 有 SQL Server、PostgreSQL 和 MySQL 等 13 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 都必須遵守的規則。
-
原子性(Atomicity)
原子性是指在事務結束時,其中所包含的更新處理要麼全部執行,要麼完全不執行,也就是要麼占有一切要麼一無所有。
例如,在之前的例子中,在事務結束時,絕對不可能出現
運動 T 恤
的價格下降了,而T 恤衫
的價格卻沒有上漲的情況。該事務的結束狀態,要麼是兩者都執行了(
COMMIT
),要麼是兩者都未執行(ROLLBACK
)。從事務中途停止的角度去考慮,就能比較容易理解原子性的重要性了。
由於用戶在一個事務中定義了兩條
UPDATE
語句,DBMS 肯定不會只執行其中一條,否則就會對業務處理造成影響。 -
一致性(Consistency)
一致性指的是事務中包含的處理要滿足資料庫提前設置的約束,如主鍵約束或者
NOT NULL
約束等。例如,設置了
NOT NULL
約束的列是不能更新為NULL
的,試圖插入違反主鍵約束的記錄就會出錯,無法執行。對事務來說,這些不合法的 SQL 會被回滾。也就是說,這些 SQL 處理會被取消,不會執行。
一致性也稱為完整性(圖 5)。
-
隔離性(Isolation)
隔離性指的是保證不同事務之間互不幹擾的特性。該特性保證了事務之間不會互相嵌套。此外,在某個事務中進行的更改,在該事務結束之前,對其他事務而言是不可見的。
因此,即使某個事務向表中添加了記錄,在沒有提交之前,其他事務也是看不到新添加的記錄的。
-
持久性(Durability)
持久性也可以稱為耐久性,指的是在事務(不論是提交還是回滾)結束後,DBMS 能夠保證該時間點的數據狀態會被保存的特性。
即使由於系統故障導致數據丟失,資料庫也一定能通過某種手段進行恢復。
如果不能保證持久性,即使是正常提交結束的事務,一旦發生了系統故障,也會導致數據丟失,一切都需要從頭再來。
保證持久性的方法根據實現的不同而不同,其中最常見的就是將事務的執行記錄保存到硬碟等存儲介質中(該執行記錄稱為日誌)。
當發生故障時,可以通過日誌恢復到故障發生前的狀態。
原文鏈接:https://www.developerastrid.com/sql/what-is-sql-transaction/
(完)