天天看點

MySQL的事務總結(事務特性,隔離級别,髒讀,不可重複讀,幻讀,常見問題)

MySQL的事務總結(事務四大特性,隔離級别,髒讀,幻讀)

MYSQL官網:https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html

1、事務(transaction)的概念

事務 是一個不可再分的最小單元,事務就是由單獨單元的一個或多個sql語句組成,在這個單元中,每個sql語句都是互相依賴的。而整個單獨單元是作為一個不可分割的整體存在,類似于實體概念中的的原子(不可分割的最小機關)。

通俗的說,事務就是一個整體,裡面的東西要麼都執行成功,要麼都不成功。不會存在部分執行成功而部分執行不成功的情況。如果事務單元中某條sql語句一旦執行失敗,那麼整個單元将會復原,所有受到影響的資料将傳回到事務開始之前的狀态,當然,如果單元中的所有sql語句都執行成功的話,那麼該事務也就被順利執行。

2、事務的四大特性(ACID)

  • 原子性 (Atomicity):事務是一個不可分割的單元,要麼同時成功,要麼同時失敗。例:當兩個人發起轉賬業務時,如果A轉賬發起,而B因為一些原因不能成功接受,事務最終将不會送出,則A和B的請求最終不會成功;
  • 一緻性 (Consistency):事務執行接收之後,資料庫完整性不被破壞。
  • 隔離性 (Isolation):多個事務之間互相隔離的,互不幹擾;
  • 持久性 (Durability):一旦事務送出,他對資料庫的改變就是永久的。注:隻要送出了事務,将會對資料庫的資料進行永久性重新整理;

3、資料庫引擎對事務的支援

在MySQL中,常見的存儲引擎有MyISAM、InnoDB、Memory 和 CSV 等。其中 InnoDB 支援事務(transaction),而MyISAM,Memory,CSV等不支援事務。

可通過 show engines 指令行來檢視MySQL的不同引擎對事務的支援:

查詢結果:

MySQL的事務總結(事務特性,隔離級别,髒讀,不可重複讀,幻讀,常見問題)

4、事務的隔離級别

資料庫事務的隔離級别有4種,由低到高分别為

  1. Read uncommitted 讀未送出
  2. Read committed 讀已送出
  3. Repeatable read 重複讀
  4. Serializable 序列化

以下是具體說明:

  • 1. Read uncommitted 讀未送出 以及 對應的髒讀問題

讀未送出,就是一個事務可以讀取另一個未送出事務的資料。

舉例:

A還錢給B,原本欠100塊,結果不小心按成了1000塊,此時1000塊已經進入B的賬戶,但是還沒有送出事務,而B這個時候正好看到賬戶多了1000塊,然後A發現轉錯了,馬上復原,然後改成100塊,再送出事務。

總結:那麼對于B來說,它看到多出的1000塊,就是髒讀;怎麼解決?提高隔離級 —> Read committed

  • 2. Read committed 讀已送出 以及 對應的不可重複讀問題

讀已送出,就是隻可以讀取到已經送出事務的資料。

舉例:

A拿着銀行卡去潇灑,當他結賬時,收費系統檢測到卡裡有500塊,而此時此刻,A的老婆在另一邊正好把錢轉走了,并送出了事務,那麼當收費系統走扣款流程時,再次檢測卡裡的金額,就發現餘額不足;

總結:有事務對資料進行更新操作時,讀操作事務要等待這個更新操作事務送出後才能讀取到資料,可以解決髒讀問題。但是,又出現了另一個問題:一個事務範圍内兩個相同的查詢,結果卻不一樣!這就是不可重複讀。怎麼解決?提高隔離級别—> Repeatable read

  • 3. Repeatable read 重複讀 以及 對應的幻讀問題

重複讀,就是在開始讀取資料(事務開啟)時,不再允許修改操作。

舉例:

A拿着銀行卡去潇灑,當他結賬時,開始事務,不再允許修改操作,而此時此刻,A的老婆就轉不了賬,那麼A就可以正常結賬,收費系統正常扣款;

總結:此隔離級别可以解決不可重複讀問題。

但是還有沒有别的問題?!

舉例:

A拿着銀行卡去潇灑,花了500塊,然後A的老婆去查他今天的消費記錄(老婆事務開啟),看到确實是花了500塊,此時此刻,A在回家的路上又吃了個飯,付了飯錢200塊,即 新增(INSERT) 了一條消費記錄,并送出了A新增的這個事務。老婆列印了一下A的消費清單(老婆事務送出),發現清單上花了700塊,多出了200塊,似乎出現了幻覺。

總結: 對于A的老婆來說,就是幻讀。怎麼解決?提高隔離級别 —> Serializable

  • 4. Serializable 序列化

最嚴格的隔離級别。在Serializable隔離級别下,所有事務按照次序依次執行,是以,髒讀、不可重複讀、幻讀都不會出現。

but ,魚和熊掌不可兼得,雖然Serializable隔離級别下的事務具有最高的安全性,但由于事務是串行執行,是以效率會大大下降,應用程式的性能會急劇降低。如果沒有特别重要的情景,一般都不會使用Serializable隔離級别。

5、關于髒讀,不可重複讀,幻讀的總結

髒讀: 讀到了别的事務復原前的髒資料。比如事務A執行過程中修改了id=1的資料,在未送出前,事務B讀取了id=1的資料,而事務A又復原了,這樣事務B就形成了髒讀。

不可重複讀: 事務A首先讀取了一條資料,然後執行邏輯的時候,事務B将這條資料改變了,然後事務A再次讀取的時候,是A的事務修改成功後的資料,發現資料不比對了,就是所謂的不可重複讀了。

幻讀: 一個事務在前後兩次查詢同一個範圍的時候,第一次查到的資料比第二次查到的資料條目不一緻,就産生了幻讀。

不同隔離級别與其存在的問題對應表:

隔離級别 産生髒讀 産生不可重複讀 産生髒讀
Read uncommitted 讀未送出
Read committed 讀已送出
Repeatable read 重複讀
Serializable 序列化

6、資料庫預設的隔離級别

mysql預設事務隔離級别為: REPEATABLE-READ 重複讀(orcale預設隔離級别為: READ COMMITTED 讀已送出);

  • 檢視目前資料庫的事務隔離級别:
  • 設定事務隔離級别(4選1):

7、 為什麼MySQL的預設隔離離别是REPEATABLE-READ 重複讀?

當設定為statement格式時,binlog記錄的是SQL的原文。又因為MySQL在主從複制的過程是通過binlog進行資料同步,如果設定為讀已送出/讀未送出隔離級别,當出現事務亂序的時候,就會導緻從庫在 SQL 執行之後,結果和主庫内容不一緻;

是以:READ COMMITTED or READ UNCOMMITTED隔離級别下,不支援binlog的statement格式。

拓展:binlog 的三種格式:
  • Statement(Statement-Based Replication,SBR):每一條會修改資料的 SQL 都會記錄在 binlog 中;
  • Row(Row-Based Replication,RBR):不記錄 SQL 語句上下文相關資訊,僅僅隻需要記錄某一條記錄被修改成什麼樣子;
  • Mixed(Mixed-Based Replication,MBR):Statement 和 Row 的混合體,系統會自動判斷該用 Statement 還是 Row:一般的語句修改使用 Statement 格式儲存 binlog;對于一些 Statement無法準确完成主從複制的操作,則采用 Row 格式儲存 binlog。

8、 為什麼一般開發過程中選擇Read Committed 隔離級别?

原因1:提升并發

因為Read Committed 在加鎖過程中,是不需要添加Gap Lock(間隙鎖)和 Next-Key Lock(記錄鎖和間隙鎖的組合,它指的是加在某條記錄以及這條記錄前面間隙上的鎖),隻需要對要修改的記錄添加行級鎖就行了。另外,Read Committed還支援半一緻讀,可以大大的減少了更新語句時行鎖的沖突。對于不滿足更新條件的記錄,可以提前釋放鎖,提升并發度;

原因2:減少死鎖發生

因為Repeatable Read會增加Gap Lock和Next-Key Lock,這就使得鎖的粒度變大了,那麼就容易導緻死鎖的機率增大。

繼續閱讀