事務控制
事務控制語句
MySQL默認設置下,事務都是自動提交的,即SQL執行後會立即執行commit
操作。
1)常用事務語句
顯式的開啓一個事務需要使用begin
、start transaction
或者執行set autocommit=0
,通過這些語句停止事務的自動提交。常用的事務控制語句如下,
語句 | 含義 |
---|---|
start transaction、begin | 顯式開啓事務 |
commit、commit work | 提交事務,對數據庫的修改成爲永久性的 |
rollback、rollback work | 回滾當前事務,撤銷正在執行的所有未提交的改變 |
savepoint identifier | 設置一個標識符爲identifier的保存點 |
release savepoint identifier | 刪除指定的事務保存點,如果該保存點不存在會拋出異常 |
rollback to identifier | 回滾到指定保存點 |
set transaction | 設置隔離級別,InnoDB提供的隔離級別有4種,見鎖筆記 |
2)差異辨析
- start transaction和 begin
兩個語句都可以顯式開啓事務,區別在於存儲過程。
在存儲過程中,MySQL的分析器會將begin
識別爲begin ... end
,因此在存儲過程中只能使用start transaction
開啓一個事務。 - commit和 commit work
都用於提交事務,不同之處在於commit work
用於控制事務結束後的行爲是chain
還是release
,如果是chain
方式,事務就成了鏈事務。
可以通過設置completion_type
變量的值來進行控制,默認爲0。在這種情況下,commit
和commit work
是等價的。
當該變量值爲1時,commit work
等價於commit work chain
,表示立刻開啓一個相同隔離級別的事務。
該變量值爲2時,等價於commit work release
,事務提交後會自動斷開與服務器的連接。
上方操作完成後,查詢表中所有記錄,只會得到a=1
的一條記錄。
設置completion_type=1
,插入a=1
後對事務進行提交,插入重複記錄2時拋出異常。之後執行rollback
操作,只留下一條記錄,因爲commit work chain
後自動開啓一個鏈事務,第二條語句在同一個事務內,所以回滾後a=2
並沒有插入到數據表中。
3)注意事項
已正確執行語句不會自動回滾
InnoDB的事務是原子的,構成事務的每條語句都會提交,或者所有語句都回滾。一條語句失敗並拋出異常時,並不會導致先前已經執行的語句回滾。必須用戶自己決定是否對已執行的語句提交或回滾,
重複插入值後,並沒有將第一次正確執行的語句所做的改變回滾。
rollback to savepoint並非結束事務
rollback to savepoint
中雖然有 rollback,但是並不是真正結束事務。執行力rollback to savepoint
後也需要顯式提交或回滾。
通過rollback to savepoint回滾到保存點t2
,事務並沒有結束,
再運行 rollback後,事務才完整回滾。
隱式提交SQL語句
下方的SQL語句會產生一個隱式提交操作,即執行完這些語句後,會有一個隱式的commit
操作,
需要注意truncate table
語句是DDL,雖然和對整張表執行delete
操作是一樣的,但truncate
無法回滾。
分佈式事務
1)XA事務構成
分佈式事務允許多個獨立的事務資源參與到一個全局事務中。事務資源通常是關係型數據庫系統。使用分佈式事務時,InnoDB存儲引擎的事務隔離級別必須設置爲serializable
。
XA事務有一個或多個資源管理器、一個事務管理器和一個應用程序組成。
- 資源管理器:提供訪問事務資源的方法,通常一個數據庫就是一個資源管理器
- 事務管理器:協調全局事務中的各個事務,需要與所有資源管理器通信
- 應用程序:定義事務邊界,指定全局事務中的操作
分佈式事務使用兩段式提交,
- 階段1:所有參與全局事務的節點都開始準備(PREPARE),告訴事務管理器已做好提交準備
- 階段2:事務管理器告知資源管理器執行 rollback 或 commit。任何一個節點如果不能被正常提交,則其餘所有事務都要回滾。
相比於本地事務,分佈式事務多了一次PREPARE操作,待收到所有節點的信息後,再進行提交或回滾。
2)XA事務語法
MySQL數據庫XA事務的SQL語法如下,
單節點上運行XA事務的例子,
事務控制不良習慣
1)循環中提交
大多數情況下,MySQL都會開啓自動提交,如果遇到循環執行的SQL語句,相當於每輪循環中都會進行一次提交。下面兩個事務過程的執行時相同的,
- 自動提交開啓的狀態下,顯式的在每輪循環中提交
- 自動提交開啓狀態下,未顯式在每輪循環中提交
- 自動提交開啓狀態下,存儲過程使用
start transaction
對上述三個過程進行調用,
相比於load1和load2中count
次提交,存儲過程load3只進行了一次提交。如果對load2的調用過程進行調整也可以達到 load3的效果,
2)不關注一個事務中語句順序
根據兩階段鎖,整個事務中涉及的鎖,需要等待事務提交時纔會釋放。同一個事務中,把沒鎖或鎖定範圍小的語句先執行,鎖定範圍大的語句後執行。
3)不關注不同事務訪問資源的順序
死鎖原因中有兩條與不同事務訪問資源的順序有關,
- 不同線程併發訪問同一張表的多行數據,未按順序訪問導致死鎖
- 不同線程併發訪問多個表時,未按順序訪問導致死鎖
4)不關注事務隔離級別
如果完全不關注業務使用的 MySQL 是什麼隔離級別,可能會降低程序的併發能力或者導致死鎖。
比如業務場景完全能接受幻讀,如果要求更高的 QPS,使用 RR 隔離級別顯然不是最好的選擇,因此可以改爲 RC 隔離級別。而如果業務使用的是 RR 隔離級別,可能由於間隙鎖導致死鎖(可參考MySQL鎖筆記中的例子),因此也應該在程序編寫時關注 RR 隔離級別下是否會有間隙鎖。
5)混合使用存儲引擎
在事務中混合使用事務型(比如 InnoDB)和非事務型(比如 MyISAM)表,如果是正常提交是什麼問題。但是,如果該事務回滾了,事務型的表可以正常回滾,而非事務型的表的變更就無法回滾了。這種情況就會導致數據不正常,並且事務最終的結果也難以確定。