MySQL InnoDB存儲引擎,實現的是基於多版本的並發控制協議——MVCC (Multi-Version Concurrency Control) (注:與MVCC相對的,是基於鎖的並發控制,Lock-Based Concurrency Control)。MVCC最大的好處,相信也是耳熟能詳:讀不加鎖,讀寫不沖突。在讀多寫少的OLTP應用中,讀寫不沖突是非常重要的,極大的增加了系統的並發性能,這也是為什么現階段,幾乎所有的RDBMS,都支持了MVCC。
對於delete操作,innodb是通過先將要刪除的那一行標記為刪除,而不是馬上清除這一行,因為innodb實現了MVCC,這些undo段用來實現MVCC多版本機制。鎖不阻塞讀,讀也不阻塞寫,這樣大大提高了並發性。那么在一致性讀的時候,怎么才能找到和事務開始的那個版本呢?
主鍵索引,每個行都有一個事務ID和一個undo ID,這個undo ID指向了這行的先前版本的位置。 非主鍵索引(輔助索引secondary index),通過先找主鍵索引再找到undo段。而對於update操作,則是先標記刪除,然后insert一個新的行,接下來如果有一致性讀,那么查找old version的行的原理和delete操作是一樣的
innoDB的行記錄格式中有6字節事務ID的和7字節的回滾指針,通過為每一行記錄添加這兩個額外的隱藏值來實現MVCC,這兩個值一個記錄這行數據何時被創建,另外一個記錄這行數據何時過期(或者被刪除)。但是InnoDB並不存儲這些事件發生時的實際時間,相反它只存儲這些事件發生時的系統版本號。這是一個隨着事務的創建而不斷增長的數字。每個事務在事務開始時會記錄它自己的系統版本號。每個查詢必須去檢查每行數據的版本號與事務的版本號是否相同。讓我們來看看當隔離級別是REPEATABLE READ時這種策略是如何應用到特定的操作的。
* SELECT: 當隔離級別是REPEATABLE READ時select操作,InnoDB必須每行數據來保證它符合兩個條件: 1、InnoDB必須找到一個行的版本,它至少要和事務的版本一樣老(也即它的版本號不大於事務的版本號)。這保證了不管是事務開始之前,或者事務創建時,或者修改了這行數據的時候,這行數據是存在的。 2、這行數據的刪除版本必須是未定義的或者比事務版本要大。這可以保證在事務開始之前這行數據沒有被刪除。 符合這兩個條件的行可能會被當作查詢結果而返回。 * INSERT:
InnoDB為這個新行記錄當前的系統版本號。 * DELETE:
InnoDB將當前的系統版本號設置為這一行的刪除ID。 * UPDATE:
InnoDB會寫一個這行數據的新拷貝,這個拷貝的版本為當前的系統版本號。它同時也會將這個版本號寫到舊行的刪除版本里。 這種額外的記錄所帶來的結果就是對於大多數查詢來說根本就不需要獲得一個鎖。他們只是簡單地以最快的速度來讀取數據,確保只選擇符合條件的行。這個方案的缺點在於存儲引擎必須為每一行存儲更多的數據,
做更多的檢查工作,處理更多的善后操作。 MVCC只工作在REPEATABLE READ和READ COMMITED隔離級別下。READ UNCOMMITED不是MVCC兼容的,因為查詢不能找到適合他們事務版本的行版本;它們每次都只能讀到最新的版本。
SERIABLABLE也不與MVCC兼容,因為讀操作會鎖定他們返回的每一行數據。
並發控制技術:
LBCC:Lock-Based Concurrency Control,基於鎖的並發控制。 MVCC:Multi-Version Concurrency Control,基於多版本的並發控制協議。純粹基於鎖的並發機制並發量低,MVCC是在基於鎖的並發控制上的改進,主要是在讀操作上提高了並發量。 在MVCC並發控制中,讀操作可以分成兩類: 1)快照讀 (snapshot read):讀取的是記錄的可見版本 (有可能是歷史版本),不用加鎖(共享讀鎖s鎖也不加,所以不會阻塞其他事務的寫)。 2)當前讀 (current read):讀取的是記錄的最新版本,並且,當前讀返回的記錄,都會加上鎖,保證其他事務不會再並發修改這條記錄。