轉載自 https://blog.csdn.net/lans_g/article/details/124232192
配合 https://www.bilibili.com/video/BV1Hr421p7EK 影片觀看更佳。
一、什麼是MVCC?
mvcc,也就是多版本併發控制,是為了在讀取資料時不加鎖來提高讀取效率和併發性的一種手段。
資料庫併發有以下幾種場景:
- 讀-讀:不存在任何問題。
- 讀-寫:有執行緒安全問題,可能出現髒讀、幻讀、不可重複讀。
- 寫-寫:有執行緒安全問題,可能存在更新丟失等。
mvcc解決的就是讀寫時的執行緒安全問題,執行緒不用去爭搶讀寫鎖。
mvcc所提到的讀是快照讀,也就是普通的select語句。快照讀在讀寫時不用加鎖,不過可能會讀到歷史資料。
還有一種讀取資料的方式是當前讀,是一種悲觀鎖的操作。它會對當前讀取的資料進行加鎖,所以讀到的資料都是最新的。主要包括以下幾種操作:
- select lock in share mode(共享鎖)
- select for update(排他鎖)
- update(排他鎖)
- insert(排他鎖)
- delete(排他鎖)
二、MVCC的實現
1.回顧事務的特性
- 原子性:透過undolog實現。
- 永續性:透過redolog實現。
- 隔離性:透過加鎖(當前讀)&MVCC(快照讀)實現。
- 一致性:透過undolog、redolog、隔離性共同實現。
2.回顧事務的隔離級別
- 讀未提交:允許讀取尚未提交的資料變更。可能會導致髒讀、幻讀或不可重複讀。
- 讀已提交:允許讀取已經提交的資料。可能會導致幻讀和不可重複讀。
- 可重複讀:對同一欄位的多次讀取結果都是一致的,除非資料是被本身事務自己所修改。可能會導致幻讀。
- 可序列化:最高隔離級別。
在讀已提交和可重複讀隔離級別下的快照讀,都是基於MVCC實現的!
3.mvcc實現原理
mvcc的實現,基於undolog、版本鏈、readview。
在mysql儲存的資料中,除了我們顯式定義的欄位,mysql會隱含的幫我們定義幾個欄位。
-
trx_id:事務id,每進行一次事務操作,就會自增1。
-
roll_pointer:回滾指標,用於找到上一個版本的資料,結合undolog進行回滾。
什麼是readview呢?
當我們用select讀取資料時,這一時刻的資料會有很多個版本(例如上圖有四個版本),但我們並不知道讀取哪個版本,這時就靠readview來對我們進行讀取版本的限制,透過readview我們才知道自己能夠讀取哪個版本。
在一個readview快照中主要包括以下這些欄位:
對readview中的引數做一些解釋
m_ids:活躍的事務就是指還沒有commit的事務。
max_trx_id:例如m_ids中的事務id為(1,2,3),那麼下一個應該分配的事務id就是4,max_trx_id就是4。
creator_trx_id:執行select讀這個操作的事務的id。
readview如何判斷版本鏈中的哪個版本可用呢?(重點!)
從上到下分別為(1)(2)(3)(4),依次進行解釋
trx_id表示要讀取的事務id
(1)如果要讀取的事務id等於進行讀操作的事務id,說明是我讀取我自己建立的記錄,那麼為什麼不可以呢。
(2)如果要讀取的事務id小於最小的活躍事務id,說明要讀取的事務已經提交,那麼可以讀取。
(3)max_trx_id表示生成readview時,分配給下一個事務的id,如果要讀取的事務id大於max_trx_id,說明該id已經不在該readview版本鏈中了,故無法訪問。
(4)m_ids中儲存的是活躍事務的id,如果要讀取的事務id不在活躍列表,那麼就可以讀取,反之不行。
4.mvcc如何實現RC和RR的隔離級別
(1)RC的隔離級別下,每個快照讀都會生成並獲取最新的readview。
(2)RR的隔離級別下,只有在同一個事務的第一個快照讀才會建立readview,之後的每次快照讀都使用的同一個readview,所以每次的查詢結果都是一樣的。
5.幻讀問題
- 快照讀:透過mvcc,RR的隔離級別解決了幻讀問題,因為每次使用的都是同一個readview。
- 當前讀:透過next-key鎖(行鎖+gap鎖),RR隔離級別並不能解決幻讀問題。