MySQL事務簡介
前言:
在學習 MySQL 的過程中,事務永遠是一項繞不開的話題,日常程序開發(fā)也經(jīng)常會用到事務。本篇文章將以 MySQL 8.0 版本為基礎,一起來深入了解下 MySQL 事務。
? 一、事務的定義
數(shù)據(jù)庫事務(Database Transaction) ,是指作為單個邏輯工作單元執(zhí)行的一系列操作,要么完整地執(zhí)行,要么完全地不執(zhí)行。事務處理可以確保除非事務性單元內(nèi)的所有操作都成功完成,否則不會永久更新面向數(shù)據(jù)的資源。通過將一組相關(guān)操作組合為一個要么全部成功要么全部失敗的單元,可以簡化錯誤恢復并使應用程序更加可靠。一個邏輯工作單元要成為事務,必須滿足所謂的 ACID(原子性、一致性、隔離性和持久性)屬性。
舉個銀行轉(zhuǎn)賬的例子: A從銀行轉(zhuǎn)賬1w塊錢給B,此時數(shù)據(jù)庫會涉及3個操作:
1.新增一條交易記錄,記錄A轉(zhuǎn)賬給B? 2.A銀行卡余額減少1w? 3.B銀行卡余額增加1w
那么這3個操作是一個整體,要么一起成功,要么一起失敗,不會存在只有部分成功。例如A銀行卡余額減少1w,但是B銀行卡余額沒有增加1w,這就會出大問題了。
? 二、事務的屬性
事務的四個屬性:
1.原子性(Atomicity)
事務必須是原子工作單元;對于其數(shù)據(jù)修改,要么全都執(zhí)行,要么全都不執(zhí)行。通常,與某個事務關(guān)聯(lián)的操作具有共同的目標,并且是相互依賴的。如果系統(tǒng)只執(zhí)行這些操作的一個子集,則可能會破壞事務的總體目標。原子性消除了系統(tǒng)處理操作子集的可能性。
2.一致性(Consistency)
事務在完成時,必須使所有的數(shù)據(jù)都保持一致狀態(tài)。在相關(guān)數(shù)據(jù)庫中,所有規(guī)則都必須應用于事務的修改,以保持所有數(shù)據(jù)的完整性。事務結(jié)束時,所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(如 B 樹索引或雙向鏈表)都必須是正確的。某些維護一致性的責任由應用程序開發(fā)人員承擔,他們必須確保應用程序已強制所有已知的完整性約束。例如,當開發(fā)用于轉(zhuǎn)帳的應用程序時,應避免在轉(zhuǎn)帳過程中任意移動小數(shù)點。
3.隔離性(Isolation)
由并發(fā)事務所作的修改必須與任何其它并發(fā)事務所作的修改隔離。事務查看數(shù)據(jù)時數(shù)據(jù)所處的狀態(tài),要么是另一并發(fā)事務修改它之前的狀態(tài),要么是另一事務修改它之后的狀態(tài),事務不會查看中間狀態(tài)的數(shù)據(jù)。這稱為隔離性,因為它能夠重新裝載起始數(shù)據(jù),并且重播一系列事務,以使數(shù)據(jù)結(jié)束時的狀態(tài)與原始事務執(zhí)行的狀態(tài)相同。當事務可序列化時將獲得最高的隔離級別。在此級別上,從一組可并行執(zhí)行的事務獲得的結(jié)果與通過連續(xù)運行每個事務所獲得的結(jié)果相同。由于高度隔離會限制可并行執(zhí)行的事務數(shù),所以一些應用程序降低隔離級別以換取更大的吞吐量。
4.持久性(Durability)
事務完成之后,它對于系統(tǒng)的影響是永久性的。該修改即使出現(xiàn)致命的系統(tǒng)故障也將一直保持。
? 三、事務的隔離級別
直接看圖:

事務的存在就是為了解決并發(fā)情況下數(shù)據(jù)一致性的問題,并發(fā)期間,可能會產(chǎn)生幾種問題:
1.臟讀(Dirty Read)
某個事務已更新一份數(shù)據(jù),另一個事務在此時讀取了同一份數(shù)據(jù),由于某些原因,前一個RollBack了操作,則后一個事務所讀取的數(shù)據(jù)就會是不正確的。
2.不可重復讀(Non-repeatable Read)
在一個事務的兩次查詢之中數(shù)據(jù)不一致,這可能是兩次查詢過程中間插入了一個事務更新的原有的數(shù)據(jù)。
3.幻讀(Phantom Read)
在一個事務的兩次查詢中數(shù)據(jù)筆數(shù)不一致,例如有一個事務查詢了幾列(Row)數(shù)據(jù),而另一個事務卻在此時插入了新的幾列數(shù)據(jù),先前的事務在接下來的查詢中,就會發(fā)現(xiàn)有幾列數(shù)據(jù)是它先前所沒有的。

? 四、MySQL中的事務
MySQL 中只有 InnoDB 、NDB Cluster 存儲引擎支持事務,其它的存儲引擎是不支持事務的。下面主要來介紹下 InnoDB 引擎下的事務。
4.1 自動提交
--?MySQL自動提交是默認開啟的
mysql>?show?variables?like?'AUTOCOMMIT';
+---------------+-------+
|?Variable_name?|?Value?|
+---------------+-------+
|?autocommit????|?ON????|
+---------------+-------+
1?row?in?set,?1?warning?(0.01?sec)
--?關(guān)閉MySQL自動提交
mysql>?set?autocommit?=0;
Query?OK,?0?rows?affected?(0.00?sec)
mysql>?show?variables?like?'AUTOCOMMIT';
+---------------+-------+
|?Variable_name?|?Value?|
+---------------+-------+
|?autocommit????|?OFF???|
+---------------+-------+
1?row?in?set,?1?warning?(0.00?sec)
--?再次開啟MySQL自動提交
mysql>?set?autocommit?=1;
Query?OK,?0?rows?affected?(0.00?sec)
4.2 隔離級別查看與修改
MySQL 的默認事務隔離級別是 REPEATABLE-READ (可重復讀)。
select @@tx_isolation; 卻提示錯誤:Unknown system variable 'tx_isolation'
在 MySQL 8.0.3 中,該變量已經(jīng)被 transaction_isolation 替換了。
--?MySQL?8.0?查詢事務隔離級別
select?@@transaction_isolation;
show?variables?like?'transaction_isolation';
調(diào)整 MySQL 的事務隔離級別:
--?查詢事務隔離級別
select?@@transaction_isolation;
--?修改當前事務隔離級別
set?session?transaction?isolation?level?READ?COMMITTED;
--?修改全局事務隔離級別
set?global?transaction?isolation?level?READ?COMMITTED;
4.3 事務的開啟與關(guān)閉
語法:
START?TRANSACTION
????[transaction_characteristic?[,?transaction_characteristic]?...]
transaction_characteristic:?{
????WITH?CONSISTENT?SNAPSHOT
??|?READ?WRITE
??|?READ?ONLY
}
BEGIN?[WORK]
COMMIT?[WORK]?[AND?[NO]?CHAIN]?[[NO]?RELEASE]
ROLLBACK?[WORK]?[AND?[NO]?CHAIN]?[[NO]?RELEASE]
SET?autocommit?=?{0?|?1}
開啟事務的三種方法:
1.autocommit set autocommit=0; 關(guān)閉自動提交 所有事務都需要手工 commit 或 rollback。
2.START TRANSACTION 開啟單個事務,不受 autocommit 影響。
3.BEGIN 開啟單個事務,不受 autocommit 影響。
--?關(guān)閉自動提交??需要手工commit?或?rollback
set?autocommit=0;
--?開啟事務
START?TRANSACTION;
BEGIN;
4.4 事務的savepoint
savepoiont 用于事務中,記錄事務的中間狀態(tài),rollback 可以回滾到指定的 savepoint 點。
create?table?t10(id?int,name?varchar(100));
begin;
insert?into?t10(id,name)?values?(1,'abc');
mysql>?select?*?from?t10;
+------+------+
|?id???|?name?|
+------+------+
|????1?|?abc??|
+------+------+
1?row?in?set?(0.00?sec)
--?保留此時的狀態(tài),命名為a
savepoint?a;
insert?into?t10(id,name)?values?(2,'def');
mysql>?select?*?from?t10;
+------+------+
|?id???|?name?|
+------+------+
|????1?|?abc??|
|????2?|?def??|
+------+------+
2?rows?in?set?(0.00?sec)
--?會滾到savepoint?a的位置
rollback?to?a;
--?此時可以看到?第一條insert成功,第二條insert回滾了
mysql>?select?*?from?t10;
+------+------+
|?id???|?name?|
+------+------+
|????1?|?abc??|
+------+------+
1?row?in?set?(0.00?sec)
推薦閱讀
(點擊標題可跳轉(zhuǎn)閱讀)
