熟悉數據庫隔離級別的人都知道,在RR(可重複讀)隔離級別下,不管什麼時候屢次執行相同的SELECT快照讀語句,獲得的結果集都是徹底同樣的,即使兩次SELECT語句執行期間,其餘事務已經改變了該查詢結果並已經提交。數據庫
對於這一機制的實現原理,網上常見的一種解釋以下:優化
####MVCC在MySQL的InnoDB中的實現 在InnoDB中,會在每行數據後添加兩個額外的隱藏的值來實現MVCC,這兩個值一個記錄這行數據什麼時候被建立,另一個記錄這行數據什麼時候過時(或者被刪除)。 在實際操做中,存儲的並非時間,而是事務的版本號,每開啓一個新事務,事務的版本號就會遞增。 在可重讀Repeatable reads事務隔離級別下: SELECT時,讀取建立版本號<=當前事務版本號,刪除版本號爲空或>當前事務版本號。 INSERT時,保存當前事務版本號爲行的建立版本號 DELETE時,保存當前事務版本號爲行的刪除版本號 UPDATE時,插入一條新紀錄,保存當前事務版本號爲行建立版本號,同時保存當前事務版本號到原來刪除的行
上述解釋確實可讓讀者簡單快速地理解MVCC機制的核心思想,我最開始也覺得本身已經徹底理解MVCC機制的實現原理了,可是當我試圖利用上述原理去解釋某些特別的實測結果時,卻發現老是難以自圓其說。spa
如上圖所示,事務2中,雖然記錄A的建立版本號1小於當前事務版本號2,可是依然沒法讀取到記錄A。指針
如上圖所示,事務2中,雖然記錄A的建立版本號小於當前事務版本號2,且記錄A已經提交,可是第二次查詢時,事務2依然沒法查詢到任何記錄。 code
如上圖所示,事務1中,雖然記錄A的建立版本號大於當前事務版本號1,可是事務1依然能夠查詢到記錄A。對象
可見,常見的對MVCC版本的實現原理的理解彷佛遺漏了某些關鍵邏輯,致使沒法解釋不少特殊狀況。blog
下面咱們來一塊兒看一下InnoDB中,MVCC機制究竟是如何控制記錄的可見性的。事務
InnoDB RR隔離界別下,MVCC對記錄可見性控制,還有以下關鍵斷定邏輯:it
1. 事務ID並不是在事務begin時就分配,而是在事務首次執行非快照讀操做(SELECT ... FOR UPDATE/IN SHARE MODE、UPDATE、DELETE)時分配。table
注: 若是事務中只有快照讀,InnoDB對只有快照讀事務有特殊優化,這類事務不會擁有事務ID,由於它們不會在系統中留下任何修改(甚至連鎖都不會建),因此也沒有留下事務ID的機會。 雖然使用SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID(); 查詢此類事務ID時,會輸出一個很大的事務ID(好比328855902652352),不過這只是MySQL在輸出時臨時隨機分配的一個用於顯示的ID而已。
2. 每一個事務首次執行快照讀操做時,會建立一個read_view對象(能夠理解爲在當前事務中,爲數據表創建了一個邏輯快照,read_view對象就是用來控制此邏輯快照的可見範圍的)。事務提交後,其建立的read_view對象將被銷燬。
read_view對象中有三個關鍵字段用於判斷記錄的可見範圍。它們分別是trx_ids、low_limit_id、up_limit_id。 1. read_view->trx_ids:建立該read_view時,記錄正活躍的其餘事務的ID集合。事務ID在集合中降序排列,便於二分查找。 2. read_view->low_limit_id:當前活躍事務中的最大事務ID+1(即系統中最近一個還沒有分配出去的事務號)。 3. read_view->up_limit_id:當前活躍事務中的最小事務ID。
3. 若是記錄的版本號比本身事務的read_view->up_limit_id小,則該記錄的當前版本必定可見。由於這些版本的內容造成於快照建立以前,且它們的事務也確定已經commit了。或者若是記錄的版本號等於本身事務的事務ID,則該記錄的當前版本也必定可見,由於該記錄版本就是本事務產生的。
4. 若是記錄的版本號與本身事務的read_view->low_limit_id同樣或比它更大,則該版本的記錄的當前版本必定不可見。由於這些版本的內容造成於快照建立以後。
不可見有以下兩層含義: 1. 若是該記錄是新增或修改後造成的新版本記錄,則對新增和修改行爲不可見,即看不到最新的內容; 2. 若是該記錄是標記爲已刪除造成的新版本記錄,則對該刪除行爲不可見,便可以看到刪除前的內容。
5. 當沒法經過4和5快速判斷出記錄的可見性時,則查找該記錄的版本號是否在本身事務的read_view->trx_ids列表中,若是在則該記錄的當前版本不可見,不然該記錄的當前版本可見。
6. 當一條記錄判斷出其當前版本不可見時,經過記錄的DB_ROLL_PTR(undo段指針),嘗試去當前記錄的undo段中提取記錄的上一個版本進行4~6中一樣的可見性判斷,若是能夠則該記錄的上一個版本可見。
4、用例的正確解釋
爲了更好的檢驗上面新增的知識,對部分用例進行了適當的擴展。
要正確理解InnoDB RR隔離級別下MVCC的可見性控制邏輯,需注意補充以下關鍵知識:
1. 事務ID並不是事務begin時分配,是延遲到須要分配時才分配的。
2. 事務在首次快照讀時建立快照,並將快照版本的可見範圍控制信息記錄在read_view對象中。