1. 程式人生 > 實用技巧 >MySql MVCC機制

MySql MVCC機制

Multiversion concurrency control 多版本併發控制
併發訪問(讀或者寫)資料庫時,對正在事務內處理的資料做多版本的管理,用來避免由於寫操作的堵塞,而引發讀操作失敗的併發問題。

MySql預設採用RR級別允許幻讀,但是不允許可重複度和髒讀

Sample

create table `test` (
	`id` int (11),
	`test_id` int (11)
); 
insert into `test` (`id`, `test_id`) values('1','18');
insert into `test` (`id`, `test_id`) values('4','8');
insert into `test` (`id`, `test_id`) values('7','4');
insert into `test` (`id`, `test_id`) values('10','1234');
set autocommit=0;

使用者1

begin;
-- 更新 id 為 1 的資料
UPDATE test SET test_id = 20 WHERE id = 1;
SELECT * FROM test WHERE id = 1;
+------+---------+
| id   | test_id |
+------+---------+
|    1 |      20 |
+------+---------+

使用者2

begin;
--查詢 id 為 1 的資料
SELECT * FROM test WHERE id = 1;
+------+---------+
| id   | test_id |
+------+---------+
|    1 |      18 |
+------+---------+

通過結果說明:我們可以在一個事務未進行 commit/rollback操作之前,另一個事務仍然可以讀取到資料庫中的資料,只不過是讀取到的是其他事務未改變之前的資料。此處是利用了MVCC多資料做了多版本處理,讀取的資料來源於快照。

什麼是MVCC

MVCC,Multi-Version Concurrency Control,多版本併發控制。MVCC 是一種併發控制的方法,一般在資料庫管理系統中,實現對資料庫的併發訪問;在程式語言中實現事務記憶體。

如果有人從資料庫中讀資料的同時,有另外的人寫入資料,有可能讀資料的人會看到『半寫』或者不一致的資料。有很多種方法來解決這個問題:

  • 最簡單的辦法:通過加鎖,讓所有的讀者等待寫者工作完成,但是這樣效率會很差。
  • MVCC方法:每個連線到資料庫的讀者,在某個瞬間看到的是資料庫的一個快照,寫者寫操作造成的變化在寫操作完成之前對於讀者都是不可見的。

當一個 MVCC 資料庫需要更新或者新增一條資料記錄的時候,它不會直接用新資料覆蓋舊資料,而是將舊資料標記為過時(obsolete)並在別處增加新版本的資料。這樣就會有儲存多個版本的資料,但是隻有一個是最新的。這種方式允許讀者讀取在他讀之前已經存在的資料,即使這些在讀的過程中半路被別人修改、刪除了,也對先前正在讀的使用者沒有影響。 需要付出的代價僅僅是需要系統週期性整理(sweep through)以真實刪除老的、過時的資料。

MVCC 併發控制下的讀事務一般使用時間戳或者事務 ID去標記當前讀的資料庫的狀態(版本)

  • 讀取資料時,讀、寫事務相互隔離,不需要加鎖。
  • 讀寫並存的時候,寫操作會根據目前資料庫的狀態,建立一個新版本,併發的讀則依舊訪問舊版本的資料

MySQL中對於 MVCC 的邏輯實現

MySql MVCC邏輯插入

在MySQL中建表時,每個表都會有三列隱藏記錄,其中和MVCC有關係的有兩列

  • 資料行的版本號 (DB_TRX_ID)
  • 刪除版本號 (DB_ROLL_PT)

在插入資料的時候,假設系統的全域性事務ID從1開始,以下SQL語句執行分析參考註釋資訊:

begin;-- 獲取到全域性事務ID
insert into `test` (`id`, `test_id`) values('5','68');
insert into `test` (`id`, `test_id`) values('6','78');
commit;-- 提交事務

當執行完以上SQL語句之後,表格中的內容會變成:

可以看到,插入的過程中會把全域性事務ID記錄到列 DB_TRX_ID 中去

MySql MVCC邏輯刪除

對上述表格做刪除邏輯,執行以下SQL語句(假設獲取到的事務邏輯ID為 3)

begin;--獲得全域性事務ID = 3
delete test where id = 6;
commit;

執行完上述SQL之後資料並沒有被真正刪除,而是對刪除版本號做改變,如下所示:

MySql MVCC邏輯更新

修改邏輯和刪除邏輯有點相似,修改資料的時候 會先複製一條當前記錄行資料,同時標記這條資料的資料行版本號為當前事務版本號,最後把原來的資料行的刪除版本號標記為當前是事務版本號。

begin;-- 獲取全域性系統事務ID 假設為 10
update test set test_id = 22 where id = 5;
commit;

執行後表格實際資料應該是:

MySql MVCC邏輯查詢

資料查詢規則如下:

  • 查詢資料行版本號早於當前事務版本號的資料行記錄
  • 查詢刪除版本號要麼為NULL,要麼大於當前事務版本號的記錄

根據上述規則,我們繼續以上張表格為例,對此做查詢操作

begin;-- 假設拿到的系統事務ID為 12
select * from test;
commit;

執行結果應該是: