天天看點

MySql讀書筆記(六): 多版本控制(MVCC)知識點總結

作者:石老師小跟班
資料庫事務問題是面試官比較愛問的,問起事務,隔離性更是重點照顧,相信大部分人都有體會吧

1.多版本控制(MVCC)

MVCC,即Multi-Version Concurrency Control (多版本并發控制)。它是一種并發控制的方法,一般在資料庫管理系統中,實作對資料庫的并發通路,在程式設計語言中實作事務記憶體。

通俗的說,資料庫中同時存在多個版本的資料,這裡的多版本是指的某一條記錄的多版本,在對某一記錄做事務操作時,需要比對事務id,去判斷讀取哪個版本的資料

其實在Mysql中innodb引擎中,會在每條記錄中添加 隐藏字段,分别是 事務id列,復原指針列,隐藏rowid列(如果表中沒有主鍵或沒有null的唯一索引的時候才會有這一列,會以這個字段生成索引樹)

2.MVCC相關知識點

2.1 事務版本号

事務每次開啟前,都會在資料庫擷取一個 自增長的事務id,可以從事務id判斷事務的執行先後順序

2.2 隐式字段

  • row_id 單調遞增的行id 不是必需的 占用6位元組
  • trx_id 記錄操作該資料事務的事務id 是必須的
  • roll_pointer 這個隐藏列就相當于一個指針,指向復原段的 undo 日志

2.3 undo log日志

undo log 日志又叫 復原日志,使用者記錄被修改前的資訊, 在表記錄修改前,會先把資料拷貝到undo log日志中,如果事務復原,即通過 undo log日志實作還原資料

用途:

  • 事務復原時,保證原子性和一緻性
  • 用 于MVCC快照讀

2.4 版本鍊

多個事務并行操作同一行記錄時,不同僚務對該行資料的修改會産生多個版本,然後通過復原指針(roll_pointer) 連成一個連結清單,如圖

MySql讀書筆記(六): 多版本控制(MVCC)知識點總結

2.5 快照讀和目前讀

  • 快照讀: 讀取的是記錄資料的可見版本,不加鎖的話,select語句都是快照讀
  • 目前讀:讀取的記錄資料的最新版本, select 加鎖for update,lock in share mode時,是目前讀,update,insert,delete 操作都是目前讀資料

2.6 read view 視圖

read view 是在事務執行sql時,産生的視圖,實際上在 innodb中,每個sql語句執行前都會得到一個read view

在事務隔離級不同的情況下,生成的read view是不同的

  • 在讀送出事務隔離級别下, read view 是在事務中執行sql語句時,每個sql都會産生自己的的 read view ,是以這也是無法避免 不可重複讀的原因
  • 在可重複讀事務隔離級别下,read view 是在啟動事務時建立時,每個sql 在執行時,都會複用這個 read view ,是以在這個隔離級别下 可以解決 不可重複讀的問題
  • 這裡面試官會問, 在可重複讀隔離級别下,是如何解決 不可重複讀的問題的,在這個級别,它解決了幻讀問題了嗎,是如何解決的?
  • 先說結論:
  • 在可重複讀隔離級别下,是通過MVCC機制,解決的不可重複讀問題的, 在大部分場景下,在這個級别下,是通過加gap鎖的機制 解決了幻讀的問題了,但是在目前讀場景下,還有會有幻讀産生的

read view 的 重要屬性

  • m_ids:目前系統那些活躍(未送出)的讀寫事務id, 資料結構為清單
  • min_limit_id: 表示在生成read view時,目前系統中活躍的讀寫事務中最小的事務id,m_ids中的最小值
  • max_limit_id:表示生成read view 時,系統中應該配置設定給下一下事務id ,m_ids中的最大值+1
  • creator_trx_id:建立目前read view 事務id

read view 比對條件

  1. 如果事務id trx_id < min_limit_id 表明生成該版本的事務在生成 read view 前,已經送出 ,是以該版本可以被目前事務通路
  2. 如果trx_id>max_limit_id 表明生成該版本的事務在生成 read view之後,是以該版本不可以被目前事務通路
  3. 如果min_limit_id=<trx_id<max_limit_id時,會有以下幾種
  4. 如果 m_ids包含trx_id ,則代表read view 生成時刻,這個事務還未送出,如果資料的trx_id 等于 create_trx_id的話,說明是自己建立的,是以可以通路
  5. 如果 m_ids 包含 trx_id ,并且不等于creator_trx_id ,則read view 生成時,事務還沒送出,也不是自己的 ,是以目前事務也是看不到的
  6. 如果 m_ids 不包括trx_id ,則說明你這個事務在 read view 生成之前就已經送出了,是以目前事務是所見的

總結一下:

  1. 版本未送出,不可見
  2. 版本已送出,但是是在視圖建立之後 送出的,不可見
  3. 版本已送出,而且是在視圖建立前送出的,可見

3 . 重複讀送出隔離級别下 解決幻讀了嗎?

結論: 解決了,但又沒有完全解決,取決你如何看待幻讀這個概念

看2個特殊的例子

例1:

MySql讀書筆記(六): 多版本控制(MVCC)知識點總結

select 加鎖

MySql讀書筆記(六): 多版本控制(MVCC)知識點總結

在事務2中添加一條資料

會發現 事務2 阻塞,由于 select 使用 for update ,對于目前的查詢來說,對全表加上了間隙鎖,是以插入被阻塞,是以在這種情況下是解決了幻讀的情況發生

例2

會出現幻讀有一種情況

MySql讀書筆記(六): 多版本控制(MVCC)知識點總結

開啟事務2 插入操作

MySql讀書筆記(六): 多版本控制(MVCC)知識點總結

這時在事務1中執行更新操作

MySql讀書筆記(六): 多版本控制(MVCC)知識點總結
這時發現,居然多出來一條資料了,這種情況是幻讀嗎? 取決于你咋了解了

原因如下:

RR級别下ReadView的生成時機是在事務中的第一次查詢,事務結束前該ReadView複用。但是如果事務中進行了目前讀的操作,比如事務一中的update,後續再查詢就會重新生成ReadView。也就是說update操作産生了目前讀,那目前讀肯定可以讀到事務2已經提了的資料,然後全部更新後再去讀就又會産生一個readview,很明顯之前的update操作對于這個readview是可見的,是以資料的條數就跟之前的不一樣了

有不疑問的小夥伴,可以在評論區留言讨論哈

繼續閱讀