MVCC (Multi-Version Concurrency Control),多版本併發控制。數據庫實現併發訪問請求,就是基於 MVCC 實現的。
首先了解下數據庫事物的隔離級別。
隔離級別
-
Read Uncommiteed 讀未提交
讀取未提交的數據,即其他事物已經修改但還未提交的數據,這是最低的隔離級別。 -
Read committed 讀已提交
讀取已提交的數據。在一個事物中,對同一條數據,可能會出現讀取不一致現象。 -
Repeatable Read 可重複讀
可重複讀取,在一個事物中,對同一條數據,確保多次讀取的結果一樣。(Mysql 默認隔離級別) -
Serializable 序列化
串行執行,數據庫中的事物都是串行執行,不能並行執行,效率最差。
隔離級別主要是爲了實現讀操作不需要加鎖, 從而提高數據庫的性能。
不同隔離級別出現的問題
隔離級別 | 髒讀 | 幻讀 | 不可重複讀 |
---|---|---|---|
讀未提交 | ✅ | ✅ | ✅ |
讀已提交 | ✅ | ✅ | |
可重複度 | ✅ | ||
序列化 |
Read Uncommiteed 和 Serializable 不需要使用多版本控制技術就可實現。
Read Uncommiteed :直接修改原始數據即可,其他事物查看數據的時候直接可以查看到,無須其他操作即可實現。
Serializable: 所有的事物都是串行執行的,只需要一個獨佔鎖即可實現。
其中Read committed 和 Repeatable Read 兩種事物隔離使用到 MVCC 進行實現的。
MVCC 實現原理
MVCC (Multi-Version Concurrency Control),多版本併發控制。MVCC 對每行數據維護多個版本。可以實現讀操作不需要加鎖,即可實現在同一個事物中多次讀取一條數據,結果都是一致的。
MySql InnoDB存儲引擎爲例,InnoDB 在表中增加了兩個隱藏字段。每個事物都是一個事物ID,其中一個列存儲了修改時的事物ID,另一個列存儲的是刪除這條數據的事物ID。每開啓一個事物都會生成一個自增的事物ID,當查詢一條數據時,都會用當前的事物ID,和隱藏列中的事物ID進行對比,然後根據不同的事物隔離級別來決定是否返回該行數據。
下面對 Select、Delete、 Insert、 Update 四種操作了解 MVCC 的實現原理
創建一個表,表中有兩個字段 id、name。MVCC 中增加兩個隱藏列 update_version、delete_version。
update_version: 存儲修改時,當前事物的ID
delete_version: 存儲刪除時,當前事物的ID。
Insert
假設當前事物的ID=1
插入一條數據時,update_version 存儲當前事物的ID,。
id | name | update_version | delete_version |
---|---|---|---|
1 | 張三 | 1 |
Update
當前事物ID=2
修改一條數據時,會新把原來的行復制一份,並把之前的那條數據delete_version 列設置成當前的事物ID 2,標記當前數據爲刪除,新插入的數據 update_version 設置成當前事物ID 2。
id | name | update_version | delete_version |
---|---|---|---|
1 | 張三 | 1 | 2 |
1 | 張小三 | 2 |
Delete
當前事物ID=4
刪除數據時,直接把 delete_version 這個字段設置爲當前的事物ID 4.
id | name | update_version | delete_version |
---|---|---|---|
1 | 張三 | 1 | 2 |
1 | 張小三 | 2 | 4 |
Select
查詢必須滿足以下兩種條件才能返回:
- 行的 update_version 版本號小於等於當前事物ID
- 行的 delete_version 版本無值或大於當前事物ID
Select 示例一
比如事物ID=3, 查詢 id=1 的數據
id | name | update_version | delete_version |
---|---|---|---|
1 | 張三 | 1 | 2 |
1 | 張小三 | 2 |
查詢條件爲:id=1 and update_version<=3 and delete_version>3
查詢出來的數據應該是 “張小三” 這條數據。
Select 示例二
假設,事物ID 3 執行的查詢時間比較長,這時事物4開始刪除這條數據,並提交,然後事物ID=3 的查詢纔開始執行,應該是能查詢到 id=1 的數據。
事物ID=4 刪除後的數據。
id | name | update_version | delete_version |
---|---|---|---|
1 | 張三 | 1 | 2 |
1 | 張小三 | 2 | 4 |
查詢條件爲:id=1 and update_version<=3 and delete_version>3
查詢結果應該爲 “張三” 這條數據。