MySQL 事務初識

MySQL 中的事務?

對 MySQL 來講,事務一般是一組包含對數據庫操做的集合。在執行時,只有在該組內的事務都執行成功,這個事務纔算執行成功,不然就算失敗。MySQL 中,事務支持是在引擎層實現的,像 MySQL 原生的 MyISAM 引擎就不支持事務,這也是被 InooDB 取代的重要緣由。mysql

爲何要有事務呢,舉個例子來講,你的帳戶有 100 元,如今想給朋友轉帳 100 元。其中就會包含兩個很重要的操做,你的帳戶減 100 元,朋友帳戶多 100 元。因爲轉帳過程當中出現失敗是很常見的,假設操做不包含在事務內,你的帳戶減錢操做成功,朋友帳戶加錢操做失敗。就會出現,你的帳戶扣錢,對方沒有收到錢的狀況。sql

再好比,在發起轉帳操做時,因爲系統須要進行像查詢餘額,計算,更新餘額的操做,若是在等待時間內,又發起了轉帳操做,但目前更新餘額的操做尚未成功,就會出現你的 100 元,能夠給別人轉帳屢次的狀況,這對於銀行來講是確定不容許的。數據庫

對於一個事務來講一般要知足四個特性,也就是一般所說的 ACID:併發

  • Atomicity - 保證在一個工做單元(就是一組操做)中全部的操做都執行成功,不然的話當前這個事務就會失敗,以前的操做都被會回滾。框架

  • Consistency - 保證一個事務被成功提交後,數據庫的狀態是從一致性狀態變成另外一個一致性狀態。性能

  • Isolation - 保證每一個事務中的操做時是獨立的,對於其餘事務沒有影響。日誌

  • Durability - 對於已經提交的事務,即便在數據庫損壞的狀況下,也不會形成數據的丟失和損壞。code

MySQL 中的事務隔離是如何實現的?

當數據庫中有多個事務同時執行時,就可能會出現髒讀,幻讀,不可重複讀的問題,爲了解決這些問題,就出現了"隔離級別"的概念。orm

事務隔離的問題:

髒讀:事務 A 中訪問了事務 B 中未提交的數據。這裏 Transaction 1 讀到了,Transaction 2 中未提交的年齡數據。blog

不可重複讀:事務 A 中屢次查詢同一數據,但因爲事務 B 在事務 A 兩次查詢中,修改了改數據的值,致使兩次查詢的結果不同。下面 Transaction 1 中的兩次查詢查詢結果是一致的,第二次讀到的 age 已經被修改的內容。

幻讀:一般發生在事務 B 對事務 A 正在讀取的內容,添加或刪除了一條數據。形成數據莫名出現或者消失的狀況。這裏 Transaction 1 中,兩次查詢的結果並不一致,第二次查詢會多出一條記錄。

事務的隔離級別:

隔離級別 解釋 可能出現的問題
讀未提交 讀未提交是指,一個事務還沒提交時,它作的變動就能被別的事務看到。 髒讀,不可重複讀,幻讀
讀提交 讀提交是指,一個事務提交以後,它作的變動纔會被其餘事務看到。 不可重複讀,幻讀
可重複讀 可重複讀是指,一個事務執行過程當中看到的數據,老是跟這個事務在啓動時看到的數據是一致的。固然在可重複讀隔離級別下,未提交變動對其餘事務也是不可見的。 幻讀
串行化 串行化,顧名思義是對於同一行記錄,「寫」會加「寫鎖」,「讀」會加「讀鎖」。當出現讀寫鎖衝突的時候,後訪問的事務必須等前一個事務執行完成,才能繼續執行。

在 MySQL 中 RR 級別引入了間隙鎖,解決了幻讀的問題。

舉一個實際的例子,來看一下這四種隔離級別對應的結果,假設表結構以下:

mysql> create table T(c int) engine=InnoDB;
insert into T(c) values(1);

此時發生的事務以下:

隔離級別 返回結果
讀未提交 V1 是 2。事務 B 雖然尚未提交,可是結果已經被 A 看到了。所以,V二、V3 也都是 2
讀提交 V1 是 1,V2 的值是 2。事務 B 的更新在提交後才能被 A 看到。因此, V3 的值也是 2。
可重複讀 則 V一、V2 是 1,V3 是 2。 V2 仍是 1 的緣由,須要遵循:事務在執行期間看到的數據先後必須是一致的。
串行化 事務 B 執行「將 1 改爲 2」時,會被鎖住。直到事務 A 提交後,事務 B 才能夠繼續執行。因此從 A 的角度看, V一、V2 值是 1,V3 的值是 2。

執行的效率會和執行的級別有關,隔離的越高,效率越低,須要在兩者間尋找平衡。

事務隔離的應用場景?

讀未提交:

  1. 這個基本沒人會選擇,連事務都構不成。

讀提交:

  1. 通常互聯網公司的隔離級別會選用這個,緣由在:
    • RR 級別下,存在間歇鎖,出現死鎖的概率比 RC 大的多。
    • RR 級別下,條件列未命中索引會鎖表。在 RC 級別下會鎖行。
    • RC 級別下,半一致性特性增長了 update 操做的併發性。

可重複讀:

  1. 數據校驗:假設你在管理一個我的銀行帳戶表。一個表存了每月月底的餘額,一個表存了帳單明細。這時候你要作數據校對,也就是判斷上個月的餘額和當前餘額的差額,是否與本月的帳單明細一致。你必定但願在校對過程當中,即便有用戶發生了一筆新的交易,也不影響你的校對結果。這時使用「可重複讀」隔離級別就很方便。事務啓動時的視圖能夠認爲是靜態的,不受其餘事務更新的影響。

串行化:

  1. 每次讀操做都會加鎖,性能不佳。

事務隔離的實現?

在實現上,數據庫裏面會建立一個視圖,訪問的時候以視圖的邏輯結果爲準。

  • 在可重複讀時的隔離級別下,視圖是在事務啓動時建立的,整個事務存在期間都會用這個視圖。
  • 在讀提交的隔離級別下,這個視圖是在每一個 SQL 語句開始執行時建立的。
  • 在讀未提交的隔離級別下,會直接返回記錄的最新值,沒有視圖的概念。
  • 串行化是直接經過加鎖的方式來避免並行訪問。

注意這個視圖不是用於查詢定義的虛擬表,而是在 InnoDB 中實現 MVCC 用到的一致性讀視圖(consistent read view),用於支持 RC 和 RR 隔離級別的實現。

可重複讀的具體實現:

在 MySQL 中,實際上每條記錄在更新時都會同時記錄一條回滾操做。記錄上的最新值,經過回滾均可以獲得前一個狀態的值。好比一個值 從 1 按照順序,被修改爲 二、三、4 ,就會在回滾日誌中有以下的記錄。

當前最新是 4,在查詢這條記錄時,不一樣時刻啓動的事務會有不一樣的 read-view. 在視圖 A B C 中,記錄值爲 1, 2, 4. 同一條記錄能夠存在多個版本,這就是數據庫多版本併發控制(MVCC)。對於 read-view A 來講,要獲得 1,就必須將當前值依次執行圖中全部的回滾操做獲得。假如,有另一個事務將 4 改爲 5,但對於視圖 A B C 來講,事務是不衝突的。

回滾段的刪除,爲何要避免使用長事務?

既然每一條記錄都會更新是都會產生一條回滾操做記錄,時間一長,確定會佔用大量的存儲空間。那麼系統會在何時刪除這些回滾日誌呢,就是在當前系統裏不存在比該回滾日誌更早的 read-view 時。

但若是系統裏存在着很老的事務視圖。因爲這些事務可能會訪問數據庫裏的任何數據,因此在事務提交以前,全部可能用到的回滾記錄都必須保留,這就可能出現佔用大量存儲空間的狀況。

在 MySQL 5.5 以前,回滾日誌和數據字典一塊兒放在 ibdata 文件裏,即便長事務被提交,回滾段被清理,文件也不會變小。

而且長事務還佔用鎖資源,也可能拖垮整個庫。

MySQL 中事務的啓動方式?

顯式啓動事務:

# 使用 START TRANSACTION 或者 BEGIN 開啓事務:
START TRANSACTION
    [transaction_characteristic [, transaction_characteristic] ...]

transaction_characteristic: {
    WITH CONSISTENT SNAPSHOT
  | READ WRITE
  | READ ONLY
}

BEGIN [WORK]

# 使用 COMMIT 來提交事務
COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE]

# 使用 ROLLBACK 來回滾事務
ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE]

隱式啓動事務:

# 設置當前事務的是否自動提交
SET autocommit = {0 | 1}

autocommit 的討論:

  • 當 autocommit=0 時,事務啓動後,不會自動關閉直到主動的輸入 COMMIT 或者 ROLLBACK 語句,或者斷開鏈接時,當前事務才結束。
    • 一些客戶端鏈接框架會默認鏈接成功後先執行一個 set autocommit=0 的命令。這就致使接下來的查詢都在事務中,若是是長鏈接,就致使了意外的長事務。
  • 當 autocommit=1 時,建議老是以這種方式啓動事務。
    • 若是擔憂多一次交互的問題,可使用 commit work and chain 語法。

多一次交互的問題,若是採用 autocommit=0 的這種方式,不須要每次輸入 begin ,減小了語句的交互次數。

查詢長事務:

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

避免長事務的方案:

  1. 在開發過程當中,減小事務範圍,少用長事務。若是沒法避免,保證邏輯日誌空間夠用,而且支持動態日誌空間的增加。
  2. 監控Innodb_trx表,發現長事務報警。

總結

在開始部分,介紹了 MySQL 中事務的概念,並回顧了事務的 ACID 的特性。接着探討了事務隔離的可能出現的髒讀,不可重複讀以及幻讀的問題,並給出了相應的解決方案-隔離級別。並分析了常見事務隔離的應用場景以及事務隔離的實現方式。

並在最後引出了回滾段的概念,以及爲何要避免使用長事務。並給出了開啓事務的方法。

參考

相關文章
相關標籤/搜索