1. 程式人生 > >MySQL--REPEATABLE-READ隔離級別下讀取到的“重復數據”

MySQL--REPEATABLE-READ隔離級別下讀取到的“重復數據”

屬於 兩個 客戶端 並發控制 存在 讀取 select 執行 create

在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:單表查詢的前提下)。

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

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

技術分享

MySQL--REPEATABLE-READ隔離級別下讀取到的“重復數據”