文章出處

在MySQL中,使用MVCC來實現REPEATABLE-READ隔離級別,由于SELECT操作不會對數據加鎖,其他回話可以修改當前回話所讀取過的數據而不會被阻塞,因此讀寫不沖突。

在MVCC并發控制中,讀操作可以分成兩類:快照讀 (snapshot read)與當前讀 (current read)。快照讀,讀取的是記錄的可見版本 (有可能是歷史版本),不用加鎖。當前讀,讀取的是記錄的最新版本,并且,當前讀返回的記錄,都會加上鎖,保證其他事務不會再并發修改這條記錄。(抄自MySQL 加鎖處理分析

當事務中進行查詢時,MySQL會把快照讀和當前讀的結果進行合并再返回給客戶端,而這個合并可能導致一些奇特的結果。

生成測試數據:

drop table tb002;
create table tb002(id int primary key,c2 int,unique index uni_c2(c2));
begin;
insert into tb002(id,c2) select 1,1;
insert into tb002(id,c2) select 2,2;
insert into tb002(id,c2) select 4,4;
commit;

假設有回話A和回話B,均使用REPEATABLE-READ隔離級別

##========================================================##

首先回話A執行SQL:

begin;
select * from tb002;

返回結果如下:

##========================================================##

然后回話B執行SQL:

begin;
delete from tb002 where id=2;
commit;

由于回話A沒有加鎖,所以回話B能順利完成刪除并提交事務,當前數據庫中無C2=2的記錄,且會話B提交事務釋放鎖。

##========================================================##

回到回話A執行SQL:

insert into tb002(id,c2) select 3,2;

由于當前數據庫中無C2=2的記錄,且其他回話沒有在此C2=2的范圍上加鎖,因此回話A可以完成C2=2的數據插入。

在回話A上再次進行查詢:

select * from tb002;

返回結果如:

C2上有唯一索引,但為什么查詢結果中仍包含兩條C2=2的記錄呢?ID=2的記錄屬于快照讀的數據,ID=3的記錄數據當前讀的數據,MySQL將當前讀和快照讀的數據進行簡單的合并后返回給客戶端,并不檢查“結果數據是否滿足唯一索引”的要求。

##========================================================##

上面的測試針對唯一索引進行,那如果針對主鍵會有啥區別呢?

將插入SQL修改為:

insert into tb002(id,c2) select 2,3;

即回話B刪除的ID值和回話A新插入的ID值相同情況下,最后的查詢結果并不會包含兩條相同ID的記錄,對于“快照讀”和“當前讀”兩個結果集存在"主鍵沖突“的情況,最終返回客戶端的結果會”丟棄“快照讀中的”老版本“記錄,保留最新版本的記錄。

可見對于主鍵不存在上述問題。

##========================================================##

總結:

在基于MVCC實現的REPEATABLE-READ隔離級別下,由于快照讀和當前讀的影響,會導致返回數據結果集超過”期望結果集“的情況,甚至返回結果集中包含重復的”唯一索引鍵“,但返回結果集中不會包含重復的“主鍵”(PS:單表查詢的前提下)。

如果在事務中包含先插入后查詢的情況,應該考慮上述問題對業務的影響。

##========================================================##

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()