MySQL之事務隔離級別和MVCC

2022-06-01 12:01:36

事務隔離級別

事務並行可能出現的問題

  • 髒寫 事務之間對增刪改互相影響
  • 髒讀 事務之間讀取其他未提交事務的資料
  • 不可重複讀 一個事務在多次執行一個select讀到的資料前後不相同。因為被別的未提交事務修改,刪除資料或資料被更新被當前事務讀取到了。
  • 幻讀 一個事務在第一次讀取正常資料,第二次讀取到其他未提交事務的insert記錄,導致讀取一個不存在的記錄。指一次讀取讀取到了之前未讀取到的資料。

事務的4個隔離級別,以及解決的問題

  • READ UNCOMMITTED 未提交讀 解決髒寫
  • READ COMMITTED提交讀 解決髒寫、髒讀
  • REPEATABLE READ可重複讀 解決髒寫、髒讀、不可重複讀
  • SERIALIAZBLE可序列化 解決髒寫、髒讀、不可重複讀、幻讀

四個隔離級別和可以解決的問題是SQL專門規定的,但是在Innodb引擎下,在可重複讀的隔離級別的下就可以直接解決幻讀的問題。

我們可以在啟動時指定系統引數修改系統預設的隔離級別,預設為可重複讀。

mysql> show variables like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set, 1 warning (0.00 sec)

MVCC

版本鏈

我們在前面就講過了undo紀錄檔,對於每次進行增刪改就會產生undo紀錄檔,這時每個資料行的roll_pointer就會指向一個undo連結串列,我們就稱其為版本鏈。我們在提一嘴,因為insert的undo紀錄檔在提交後是沒有用的,所以在事務提交後insert的undo就會被釋放。

可以到前面的文章瞭解一下undo紀錄檔。大概知道undo紀錄檔的型別和產生的過程就OK了感覺怎麼儲存undo紀錄檔的那一部分講得雲裡霧裡https://www.cnblogs.com/duizhangz/p/16333565.html

為什麼沒用?因為插入並不維護舊值,只是表明一個插入,並不需要儲存什麼資訊。所以在事務提交時直接釋放掉。因為事務在回滾時需要由一條insert語句型別的undo進行回滾。

這就是上面倆事務生成的版本鏈。

undo連結串列頭儲存的就是最新事務更新的記錄資訊。

ReadView

對於不同的事務隔離級別,我們可以讀取的記錄資料是不一樣的。

  • 對於未提交讀的隔離級別來說,我們可以直接讀到資料的最新版本。

  • 對於提交讀的隔離級別來說,我們需要可以讀到的就是已經提交的事務的修改資料。

  • 對於可重複讀的隔離級別來說,我們需要可以讀到在事務開啟前已經提交的事務的資料。

  • 對於序列讀的隔離節別來說,Innodb採用加鎖的方式來保證序列讀。

對於中間兩個隔離級別,就需要ReadView這個結構來實現MVCC。以下是ReadView的結構

  • m_ids : 表示在生成ReadView時當前系統中活躍的讀寫事務ID的列表
  • min_trx_id : 表示在生成ReadView時當前系統中活躍的讀寫事務ID的最小值即在m_ids中最小的事務ID。
  • max_trx_id : 表示生成ReadView時系統中應該分配給下一個事務的ID。
  • creator_trx_id : 表示生成該ReadView的事務ID。

有了ReadView這個結構,我們在就可以對事務進行控制。

  • 當被存取的記錄行的事務ID大於等於max_trx_id,說明當前資料行不可見。
  • 當被存取的記錄行的事務ID小於min_trx_id,可以直接獲得資料。
  • 當被存取的記錄行的事務ID等於creator_trx_id,說明是當前事務修改的記錄,可以直接存取。
  • 當被存取的記錄行的事務ID大於min_trx_id且小於max_trx_id,我們需要判斷一下這個事務ID是不是在m_ids列表中,因為由可能是一個很早的事務很久還執行不介紹,導致中間的事務都結束了,如果在m_ids列表中,說明是活躍的,當前記錄行不能存取,否則可以存取資料行。

當前情況在版本鏈中從頭到尾遍歷,直到獲得到資料。

上面提到的提交讀和可重複讀,因為是兩個隔離級別有區別的,兩個隔離級別的實現只要在ReadView的時機上進行把控,就可以實現。

  • 提交讀,我們只要保證當前事務的每個語句能讀到已經提交的事務的資料。就可以在每個查詢資料進行前,事務就會建立一個ReadView。
  • 可重複讀,我們只要保證當前事務每個語句能讀到事務開啟前的資料,就可以在事務第一次讀取資料時會建立ReadView,然後整個事務只會使用這個ReadView去判斷能讀取的資料行。

仔細品一品,一下就可以恍然大悟。

這個MVCC只會在我們使用普通select查詢才會生效。