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使用。
mvcc可以認為是行級鎖的乙個變種,它可以在很多情況下避免加鎖操作,因此開銷更低。mvcc的實現大都都實現了非阻塞的讀操作,寫操作也只鎖定必要的行。innodb的mvcc實現,是通過儲存資料在某個時間點的快照來實現的。乙個事務,不管其執行多長時間,其內部看到的資料是一致的。也就是事務在執行的過程中不會相互影響。下面我們簡述一下mvcc在innodb中的實現。
innodb的mvcc,通過在每行記錄後面儲存兩個隱藏的列來實現:乙個儲存了行的建立時間,乙個儲存行的過期時間(刪除時間),當然,這裡的時間並不是時間戳,而是系統版本號,每開始乙個新的事務,系統版本號就會遞增。在rr隔離級別下,mvcc的操作如下:
select操作。
innodb只查詢版本早於(包含等於)當前事務版本的資料行。可以確保事務讀取的行,要麼是事務開始前就已存在,或者事務自身插入或修改的記錄。
行的刪除版本要麼未定義,要麼大於當前事務版本號。可以確保事務讀取的行,在事務開始之前未刪除。
insert操作。將新插入的行儲存當前版本號為行版本號。
delete操作。將刪除的行儲存當前版本號www.cppcns.com為刪除標識。
update操作。變為insert和delete操作的組合,insert的行儲存當前版本號為行版本號,delete則儲存當前版本號到原來的行作為刪除標識。
由於舊資料並不真正的刪除,所以必須對這些資料進行清理,innodb會開啟乙個後台執行緒執行清理工作,具體的規則是將刪除版本號小於當前系統版本的行刪除,這個過程叫做purge。
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;
對應在資料中的表如下(後面兩列是隱藏列,我們通過查詢語句並看不到)
select
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 語句檢索出來的資料都如下:
update
innodb執行update,實際上是新插入了一行記錄,並儲存其建立時間為當前事務的id,同時儲存當前事務id到要update的行的刪除時間.
假設3假設在執行完事務2的(1)後又執行,其它使用者執行了事務3,4,這時,又有乙個使用者對這張表執行了update操作:
第5個事務:
start transaction;
update yang set name='longwww.cppcns.com' where id=2;
commit;
根據upwww.cppcns.comdate的更新原則:會生成新的一行,並在原來要修改的列的刪除時間列上新增本事務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呢 可以認...