1. 程式人生 > 其它 >MySQL系列3-事務隔離級別

MySQL系列3-事務隔離級別

事務就是要保證一組資料庫操作,要麼全部成功,要麼全部失敗。在 MySQL 中,事務支援是在引擎層實現的。你現在知道,MySQL 是一個支援多引擎的系統,但並不是所有的引擎都支援事務。比如 MySQL 原生的 MyISAM 引擎就不支援事務,這也是 MyISAM 被 InnoDB 取代的重要原因之一。

ACID

提到事務,你肯定會想到 ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔離性、永續性)事務的基本要素

  1. 原子性(Atomicity):事務開始後所有操作,要麼全部做完,要麼全部不做,不可能停滯在中間環節。事務執行過程中出錯,會回滾到事務開始前的狀態,所有的操作就像沒有發生一樣。也就是說事務是一個不可分割的整體,就像化學中學過的原子,是物質構成的基本單位。
  2. 一致性(Consistency):事務開始前和結束後,資料庫的完整性約束沒有被破壞 。比如A向B轉賬,不可能A扣了錢,B卻沒收到。
  3. 隔離性(Isolation):同一時間,只允許一個事務請求同一資料,不同的事務之間彼此沒有任何干擾。比如A正在從一張銀行卡中取錢,在A取錢的過程結束前,B不能向這張卡轉賬。
  4. 永續性(Durability):事務完成後,事務對資料庫的所有更新將被儲存到資料庫,不能回滾。

事務的併發問題

  • 髒讀:事物1讀取到事物2到回滾事物
  • 不可重複讀:前後讀取的資訊不一致
  • 幻讀:事物1讀取到事物2插入或刪除的變更事物

不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表

隔離級別

MySQL預設的隔離級別是 可重複讀(repeatable-read)
Oracle預設的隔離級別是 讀已提交(read-committed)

檢視資料庫預設隔離級別

show variables like 'transaction_isolation';
事務隔離級別 髒讀 不可重複讀 幻讀
讀未提交(read-uncommitted)
讀已提交(read-committed)
可重複讀(repeatable-read)
序列化(serializable)

實現原理

在實現上,資料庫裡面會建立一個檢視,訪問的時候以檢視的邏輯結果為準。

  • “讀未提交”隔離級別下直接返回記錄上的最新值,沒有檢視概念;
  • “讀已提交”隔離級別下,這個檢視是在每個 SQL 語句開始執行的時候建立的。
  • “可重複讀”隔離級別下,這個檢視是在事務啟動時建立的,整個事務存在期間都用這個檢視。
  • “序列化”隔離級別下直接用加鎖的方式來避免並行訪問。

具體實現

MVCC:多版本併發控制,通過undo log版本鏈和read-view實現事務隔離

在 MySQL 中,實際上每條記錄在更新的時候都會同時記錄一條回滾操作。記錄上的最新值,通過回滾操作,都可以得到前一個狀態的值。資料變更記錄在redo log,資料回滾記錄在undo log

假設一個值從 1 被按順序改成了 2、3、4,在回滾日誌裡面就會有類似下面的記錄。

不同時刻啟動的事務會有不同的 read-view。如圖中看到的,在檢視 A、B、C 裡面,這一個記錄的值分別是 1、2、4,同一條記錄在系統中可以存在多個版本,就是資料庫的多版本併發控制(MVCC)

redo log不會一直保留,系統會判斷,當沒有事務再需要用到這些回滾日誌時,回滾日誌會被刪除。什麼時候才不需要了呢?就是當系統裡沒有比這個回滾日誌更早的 read-view 的時候。

基於上面的說明,我們來討論一下為什麼建議你儘量不要使用長事務。

  • 長事務意味著系統裡面會存在很老的事務檢視。由於這些事務隨時可能訪問資料庫裡面的任何資料,所以這個事務提交之前,資料庫裡面它可能用到的回滾記錄都必須保留,這就會導致大量佔用儲存空間。
    在 MySQL 5.5 及以前的版本,回滾日誌是跟資料字典一起放在 ibdata 檔案裡的,即使長事務最終提交,回滾段被清理,檔案也不會變小。
  • 長事務還佔用鎖資源,也可能拖垮整個庫

事務啟動方式

  1. 顯式啟動事務語句, begin 或 start transaction。配套的提交語句是 commit,回滾語句是 rollback。
  2. set autocommit=0,這個命令會將這個執行緒的自動提交關掉。意味著如果你只執行一個 select 語句,這個事務就啟動了,而且並不會自動提交。這個事務持續存在直到你主動執行 commit 或 rollback 語句,或者斷開連線。

長事務監控

可以在 information_schema 庫的 innodb_trx 這個表中查詢長事務

-- 查詢超過60秒的事務
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60