天天看點

【MySQL】可重複讀模式下 unique key失效案例

一 【背景】

【MySQL】可重複讀模式下 unique key失效案例

登博提示了幾個細節:

   1. code上的uk并未失效。

   2. rr隔離級别。

   3. 有并發線程的操作。

二 【原理分析】

1 事務隔離級别的基礎知識:

 未送出讀(read uncommitted):允許髒讀,也就是可能讀取到其他會話中未送出事務修改的資料。

 送出讀(read committed):隻能讀取到已經送出的資料。oracle等多數資料庫預設都是該級别 (不重複讀)。

 可重複讀(repeated read):可重複讀。在同一個事務内的查詢都是事務開始時刻一緻的,innodb預設級别。在sql标準中,該隔離級别消除了不可重複讀,但是還存在幻象讀。

 串行讀(serializable):完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫互相都會阻塞。

 這裡重點說一下rr 模式:可重複讀 是指在一個事務内,多次讀同一資料。在這個事務還沒有結束時,另外一個事務也通路該同一資料。那麼,在第一個事務中的兩次讀資料之間,即使第二個事務對資料進行修改,第一個事務兩次讀到的的資料是一樣的。這樣就發生了在一個事務内兩次讀到的資料是一樣的,是以稱為是可重複讀。

2 mvcc 的讀操作

   在mvcc并發控制中,讀操作可以分成兩類:快照讀 (snapshot read)與目前讀 (current read)。快照讀,讀取的是記錄的可見版本 (有可能是曆史版本),不用加鎖。

目前讀,讀取的是記錄的最新版本,并且,目前讀傳回的記錄,都會加上鎖,保證其他事務不會再并發修改這條記錄。

快照讀:簡單的select操作,屬于快照讀,不加鎖。

select * from table where ?;

目前讀:特殊的讀操作,插入/更新/删除操作,屬于目前讀,需要加鎖。

select * from table where ? lock in share mode;

select * from table where ? for update;

insert into table values (…);

update table set ? where ?;

delete from table where ?;

所有以上的語句,都屬于目前讀,讀取記錄的最新版本。并且,讀取之後,還需要保證其他并發事務不能修改目前記錄,對讀取記錄加鎖。其中,除了第一條語句,對讀取記錄加s鎖 (共享鎖)外,其他的操作,都加的是x鎖 (排它鎖)。

注意:insert操作可能會觸發unique key的沖突檢查,也會進行一個目前讀。

三【解決.複現】

測試版本: 5.5.18 5.6.16 均可複現。

現在我們根據上述理論資訊進行複現問題,具體的實作步驟如下:

注意 資料庫的隔離級别為rr 

session 1

session 2

root@test 08:47:41>set global tx_isolation='repeatable-read';

query ok, 0 rows affected (0.00 sec)

root@test 08:53:16>set autocommit=0;

root@test 08:53:22>insert into yy values(1,20,13);

query ok, 1 row affected (0.00 sec)

root@test 08:53:31>commit;

root@test 08:53:39>select * from yy;

+----+------+------+

| id | code | val  |

|  1 |   20 |   13 |

1 row in set (0.00 sec)

root@test 08:53:46>select * from yy;

root@test 08:53:53>delete from yy where id=1;

root@test 08:53:59>commit;

root@test 08:54:10>insert into yy values(2,20,13);

query ok, 1 row affected (5.59 sec)

root@test 08:54:23>select * from yy;

|  2 |   20 |   13 |

2 rows in set (0.00 sec)

當session 2中将id=1 的删除之後,session1 進行insert操作時,觸發unique key沖突檢查,此時因為id=1 code=20的資料已經被實體删除了,mysql 檢查無沖突,進行insert insert into yy values(2,20,13); 便成功了。

四【結果展示】

【MySQL】可重複讀模式下 unique key失效案例

繼續閱讀