MYSQL MVCC實現原理詳解


MVCC(Multi Version Concurrency Control的簡稱),代表多版本並發控制。與MVCC相對的,是基於鎖的並發控制,Lock-Based Concurrency Control)。
MVCC最大的優勢:讀不加鎖,讀寫不沖突。在讀多寫少的OLTP應用中,讀寫不沖突是非常重要的,極大的增加了系統的並發性能

了解MVCC前,我們先學習下Mysql架構和數據庫事務隔離級別

MYSQL 架構

 

MySQL從概念上可以分為四層,頂層是接入層,不同語言的客戶端通過mysql的協議與mysql服務器進行連接通信,接入層進行權限驗證、連接池管理、線程管理等。下面是mysql服務層,包括sql解析器、sql優化器、數據緩沖、緩存等。再下面是mysql中的存儲引擎層,mysql中存儲引擎是基於表的。最后是系統文件層,保存數據、索引、日志等。

事務隔離級別

大家都知道數據庫事務具備ACID特性,即Atomicity(原子性) Consistency(一致性), Isolation(隔離性), Durability(持久性)

原子性:要執行的事務是一個獨立的操作單元,要么全部執行,要么全部不執行

一致性:事務的一致性是指事務的執行不能破壞數據庫的一致性,一致性也稱為完整性。一個事務在執行后,數據庫必須從一個一致性狀態轉變為另一個一致性狀態。

隔離性:多個事務並發執行時,一個事務的執行不應影響其他事務的執行,SQL92規范中對隔離性定義了不同的隔離級別:

讀未提交(READ UNCOMMITED)->讀已提交(READ COMMITTED)->可重復讀(REPEATABLE READ)->序列化(SERIALIZABLE)。隔離級別依次增強,但是導致的問題是並發能力的減弱。

隔離級別 臟讀 不可重復讀 幻讀 概念
READ UNCOMMITED 事務能夠看到其他事務沒有提交的修改,當另一個事務又回滾了修改后的情況,又被稱為臟讀dirty read
READ COMMITTED × 事務能夠看到其他事務提交后的修改,這時會出現一個事務內兩次讀取數據可能因為其他事務提交的修改導致不一致的情況,稱為不可重復讀
REPEATABLE READ × × 事務在兩次讀取時讀取到的數據的狀態是一致的
SERIALIZABLE × × × 可重復讀中可能出現第二次讀讀到第一次沒有讀到的數據,也就是被其他事務插入的數據,這種情況稱為幻讀phantom read, 該級別中不能出現幻讀

大多數數據庫系統的默認隔離級別都是READ COMMITTED(但MySQL不是),InnoDB存儲引擎默認隔離級別REPEATABLE READ,通過多版本並發控制(MVCC,Multiversion Concurrency Control)解決了幻讀的問題。

MYSQL 事務日志

事務日志可以幫助提高事務的效率。使用事務日志,存儲引擎在修改表的數據時只需要修改其內存拷貝,再把該修改行為記錄到持久在硬盤上的事務日志中,而不用每次都將修改的數據本身持久到磁盤。事務日志采用的是追加的方式,因此寫日志的操作是磁盤上一小塊區域內的順序I/O,而不像隨機I/O需要在磁盤的多個地方移動磁頭,所以采用事務日志的方式相對來說要快得多。事務日志持久以后,內存中被修改的數據在后台可以慢慢地刷回到磁盤。目前大多數存儲引擎都是這樣實現的,我們通常稱之為預寫式日志(Write-Ahead Logging),修改數據需要寫兩次磁盤。
如果數據的修改已經記錄到事務日志並持久化,但數據本身還沒有寫回磁盤,此時系統崩潰,存儲引擎在重啟時能夠自動恢復這部分修改的數據。

MySQL Innodb中跟數據持久性、一致性有關的日志,有以下幾種:

  • Bin Log:是mysql服務層產生的日志,常用來進行數據恢復、數據庫復制,常見的mysql主從架構,就是采用slave同步master的binlog實現的
  • Redo Log:記錄了數據操作在物理層面的修改,mysql中使用了大量緩存,修改操作時會直接修改內存,而不是立刻修改磁盤,事務進行中時會不斷的產生redo log,在事務提交時進行一次flush操作,保存到磁盤中。當數據庫或主機失效重啟時,會根據redo log進行數據的恢復,如果redo log中有事務提交,則進行事務提交修改數據。
  • Undo Log: 除了記錄redo log外,當進行數據修改時還會記錄undo log,undo log用於數據的撤回操作,它記錄了修改的反向操作,比如,插入對應刪除,修改對應修改為原來的數據,通過undo log可以實現事務回滾,並且可以根據undo log回溯到某個特定的版本的數據,實現MVCC

MVCC實現

MVCC是通過在每行記錄后面保存兩個隱藏的列來實現的。這兩個列,一個保存了行的創建時間,一個保存行的過期時間(或刪除時間)。當然存儲的並不是實際的時間值,而是系統版本號(system version number)。每開始一個新的事務,系統版本號都會自動遞增。事務開始時刻的系統版本號會作為事務的版本號,用來和查詢到的每行記錄的版本號進行比較。
下面看一下在REPEATABLE READ隔離級別下,MVCC具體是如何操作的。

  • SELECT

    InnoDB會根據以下兩個條件檢查每行記錄:

    • InnoDB只查找版本早於當前事務版本的數據行(也就是,行的系統版本號小於或等於事務的系統版本號),這樣可以確保事務讀取的行,要么是在事務開始前已經存在的,要么是事務自身插入或者修改過的。
    • 行的刪除版本要么未定義,要么大於當前事務版本號。這可以確保事務讀取到的行,在事務開始之前未被刪除。

    只有符合上述兩個條件的記錄,才能返回作為查詢結果

  • INSERT

    InnoDB為新插入的每一行保存當前系統版本號作為行版本號。

  • DELETE

    InnoDB為刪除的每一行保存當前系統版本號作為行刪除標識。

  • UPDATE

    InnoDB為插入一行新記錄,保存當前系統版本號作為行版本號,同時保存當前系統版本號到原來的行作為行刪除標識。
    保存這兩個額外系統版本號,使大多數讀操作都可以不用加鎖。這樣設計使得讀數據操作很簡單,性能很好,並且也能保證只會讀取到符合標准的行,不足之處是每行記錄都需要額外的存儲空間,需要做更多的行檢查工作,以及一些額外的維護工作

舉例說明

create table mvcctest( id int primary key auto_increment, name varchar(20)); 

transaction 1:

start transaction; insert into mvcctest values(NULL,'mi'); insert into mvcctest values(NULL,'kong'); commit; 

假設系統初始事務ID為1;

ID NAME 創建時間 過期時間
1 mi 1 undefined
2 kong 1 undefined

transaction 2:

start transaction; select * from mvcctest; //(1) select * from mvcctest; //(2) commit 

SELECT

假設當執行事務2的過程中,准備執行語句(2)時,開始執行事務3:

transaction 3:

start transaction; insert into mvcctest values(NULL,'qu'); commit; 
ID NAME 創建時間 過期時間
1 mi 1 undefined
2 kong 1 undefined
3 qu 3 undefined

事務3執行完畢,開始執行事務2 語句2,由於事務2只能查詢創建時間小於等於2的,所以事務3新增的記錄在事務2中是查不出來的,這就通過樂觀鎖的方式避免了幻讀的產生

UPDATE

假設當執行事務2的過程中,准備執行語句(2)時,開始執行事務4:

transaction session 4:

start transaction;
update mvcctest set name = 'fan' where id = 2;
commit;

InnoDB執行UPDATE,實際上是新插入了一行記錄,並保存其創建時間為當前事務的ID,同時保存當前事務ID到要UPDATE的行的刪除時間

ID NAME 創建時間 過期時間
1 mi 1 undefined
2 kong 1 4
2 fan 4 undefined

事務4執行完畢,開始執行事務2 語句2,由於事務2只能查詢創建時間小於等於2的,所以事務修改的記錄在事務2中是查不出來的,這樣就保證了事務在兩次讀取時讀取到的數據的狀態是一致的

DELETE

假設當執行事務2的過程中,准備執行語句(2)時,開始執行事務5:

transaction session 5:

start transaction; delete from mvcctest where id = 2; commit; 
ID NAME 創建時間 過期時間
1 mi 1 undefined
2 kong 1 5

事務5執行完畢,開始執行事務2 語句2,由於事務2只能查詢創建時間小於等於2、並且過期時間大於等於2,所以id=2的記錄在事務2 語句2中,也是可以查出來的,這樣就保證了事務在兩次讀取時讀取到的數據的狀態是一致的

參考:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM