mysql的MVCC(多版本併發控制)

2022-07-01 18:51:09 字數 4467 閱讀 3719

mvcc全稱是:multiversion concurrency control,多版本併發控制,提供併發訪問資料庫時,對事務內讀取的到的記憶體做處理,用來避免寫操作堵塞讀操作的併發問題。

舉個例子,程式設計師a正在讀資料庫中某些內容,而程式設計師b正在給這些內容做修改(假設是在乙個事務內修改,大概持續10s左右),a在這10s內 則可能看到乙個不一致的資料,在b沒有提交前,如何讓a能夠一直讀到的資料都是一致的呢?

有幾種處理方法,第一種: 基於鎖的併發控制,程式設計師b開始修改資料時,給這些資料加上鎖,程式設計師a這時再讀,就發現讀取不了,處於等待情況,只能等b操作完才能讀資料,這保證a不會讀到乙個不一致的資料,但是這個會影響程式的執行效率。還有一種就是:mvcc,每個使用者連線資料庫時,看到的都是某一特定時刻的資料庫快照,在b的事務沒有提交之前,a始終讀到的是某一特定時刻的資料庫快照,不會讀到b事務中的資料修改情況,直到b事務提交,才會讀取b的修改內容。

乙個支援mvcc的資料庫,在更新某些資料時,並非使用新資料覆蓋舊資料,而是標記舊資料是過時的,同時在其他地方新增乙個資料版本。因此,同乙份資料有多個版本儲存,但只有乙個是最新的。

mvcc提供了 時間一致性的 處理思路,在mvcc下讀事務時,通常使用乙個時間戳或者事務id來確定訪問哪個狀態的資料庫及哪些版本的資料。讀事務跟寫事務彼此是隔離開來的,彼此之間不會影響。假設同乙份資料,既有讀事務訪問,又有寫事務操作,實際上,寫事務會新建乙個新的資料版本,而讀事務訪問的是舊的資料版本,直到寫事務提交,讀事務才會訪問到這個新的資料版本。

mvcc有兩種實現方式,第一種實現方式是將資料記錄的多個版本儲存在資料庫中,當這些不同版本資料不再需要時,垃圾收集器**這些記錄。這個方式被postgresql和firebird/interbase採用,sql server使用的類似機制,所不同的是舊版本資料不是儲存在資料庫中,而儲存在不同於主資料庫的另外乙個資料庫tempdb中。第二種實現方式只在資料庫儲存最新版本的資料,但是會在使用undo時動態重構舊版本資料,這種方式被oracle和mysql/innodb使用。

2、innodb的mvcc實現機制

mvcc可以認為是行級鎖的乙個變種,它可以在很多情況下避免加鎖操作,因此開銷更低。mvcc的實現大都都實現了非阻塞的讀操作,寫操作也只鎖定必要的行。innodb的mvcc實現,是通過儲存資料在某個時間點的快照來實現的。乙個事務,不管其執行多長時間,其內部看到的資料是一致的。也就是事務在執行的過程中不會相互影響。下面我們簡述一下mvcc在innodb中的實現。

innodb的mvcc,通過在每行記錄後面儲存兩個隱藏的列來實現:乙個儲存了行的建立時間,乙個儲存行的過期時間(刪除時間),當然,這裡的時間並不是時間戳,而是系統版本號,每開始乙個新的事務,系統版本號就會遞增。在rr隔離級別下,mvcc的操作如下:

select操作。

insert操作。將新插入的行儲存當前版本號為行版本號。

delete操作。將刪除的行儲存當前版本號為刪除標識。

update操作。變為insert和delete操作的組合,insert的行儲存當前版本號為行版本號,delete則儲存當前版本號到原來的行作為刪除標識。

由於舊資料並不真正的刪除,所以必須對這些資料進行清理,innodb會開啟乙個後台執行緒執行清理工作,具體的規則是將刪除版本號小於當前系統版本的行刪除,這個過程叫做purge。

3、簡單的小例子

create table yang( 

id int primary key auto_increment,

name varchar(20));

}

假設系統的版本號從1開始.

insert

innodb為新插入的每一行儲存當前系統版本號作為版本號. 

第乙個事務id為1;

start transaction;

insert into yang values(null,'yang') ;

insert into yang values(null,'long');

insert into yang values(null,'fei');

commit;

對應在資料中的表如下(後面兩列是隱藏列,我們通過查詢語句並看不到)

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

a.innodb只會查詢版本早於當前事務版本的資料行(也就是,行的系統版本號小於或等於事務的系統版本號),這樣可以確保事務讀取的行,要麼是在事務開始前已經存在的,要麼是事務自身插入或者修改過的.

b.行的刪除版本要麼未定義,要麼大於當前事務版本號,這可以確保事務讀取到的行,在事務開始之前未被刪除.

只有a,b同時滿足的記錄,才能返回作為查詢結果.

delete

innodb會為刪除的每一行儲存當前系統的版本號(事務的id)作為刪除標識.

看下面的具體例子分析:

第二個事務,id為2;

start transaction;

select * from yang; //(1)

select * from yang; //(2)

commit;

假設1假設在執行這個事務id為2的過程中,剛執行到(1),這時,有另乙個事務id為3往這個表裡插入了一條資料; 

第三個事務id為3;

start transaction;

insert into yang values(null,'tian');

commit;

這時表中的資料如下:

然後接著執行事務2中的(2),由於id=4的資料的建立時間(事務id為3),執行當前事務的id為2,而innodb只會查詢事務id小於等於當前事務id的資料行,所以id=4的資料行並不會在執行事務2中的(2)被檢索出來,在事務2中的兩條select 語句檢索出來的資料都只會下表:

假設2假設在執行這個事務id為2的過程中,剛執行到(1),假設事務執行完事務3後,接著又執行了事務4; 

第四個事務:

start   transaction;  

delete from yang where id=1;

commit;

此時資料庫中的表如下:

接著執行事務id為2的事務(2),根據select 檢索條件可以知道,它會檢索建立時間(建立事務的id)小於當前事務id的行和刪除時間(刪除事務的id)大於當前事務的行,而id=4的行上面已經說過,而id=1的行由於刪除時間(刪除事務的id)大於當前事務的id,所以事務2的(2)select * from yang也會把id=1的資料檢索出來.所以,事務2中的兩條select 語句檢索出來的資料都如下:

innodb執行update,實際上是新插入了一行記錄,並儲存其建立時間為當前事務的id,同時儲存當前事務id到要update的行的刪除時間.

假設3

假設在執行完事務2的(1)後又執行,其它使用者執行了事務3,4,這時,又有乙個使用者對這張表執行了update操作:

第5個事務:

start  transaction;

update yang set name='long' where id=2;

commit;

根據update的更新原則:會生成新的一行,並在原來要修改的列的刪除時間列上新增本事務id,得到表如下:

繼續執行事務2的(2),根據select 語句的檢索條件,得到下表:

還是和事務2中(1)select 得到相同的結果.

mysql多版本併發控制MVCC

innodb的mvcc是通過在每行記錄的後面儲存兩個隱藏的列來實現的,這兩個列乙個儲存行的建立時間,乙個儲存行的過期時間。但是並不是儲存時間而是儲存版本號,每開始乙個新的事務,版本號會自動遞增。事務開始時刻的系統版本號會作為事務的版本號,用來和查詢到每行記錄的版本號進行比較。select innod...

mysql多版本併發控制MVCC

set global transaction isolation level read committed 全域性的 set session transaction isolation level read committed 當前會話 複製 set autocommit 1 自動提交,為0手動提交...

MySQL學習 多版本併發mvcc

mysql中的大多數事務性儲存引擎實現的都不是簡單的行級鎖。基於提公升併發效能的考慮,他們一般實現了多版本併發控制 mvcc 不僅是mysql,包括oracle,postgresql等其他資料庫也實現了mvcc,但各自的實現機制不盡相同,應為mvcc沒有乙個統一的實標準。那麼什麼是mvvc呢 可以認...