事務隔離級別
事務併發可能出現的問題
- 髒寫 事務之間對增刪改互相影響
- 髒讀 事務之間讀取其他未提交事務的資料
- 不可重複讀 一個事務在多次執行一個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查詢才會生效。