天天看點

[MySQL bug] unique key corruption again…..

最近percona的研發人員report了一個uk corruption的bug,這個bug不同于之前發現的bug(見我的另外一篇部落格http://mysqllover.com/?p=1041),而是影響從5.1 到5.8全系列mysql版本,應該算是設計上的缺陷吧。

建立測試表

0. 停止purge操作:set global innodb_purge_stop_now = on; 防止标記删除的記錄被purge掉

删除表上的資料:delete from t1;  //這時候表上實體記錄還存在,隻是被标記删除了。

1. session 1: replace into t1 values (1,2);

由于有一條标記删除的記錄,檢查pk上duplicate key,在聚集索引上加鎖:mode=1027 (1024 +3 = lock_rec_not_gap | lock_x)

堆棧:

檢查二級索引duplicate key,由于存在标記删除的記錄,這裡需要加鎖,類型為x鎖

當完成duplicate key檢測後 (當然這裡是成功的),我們讓session1稍微等一會(設定debug sync同步點)

2. session 2:replace into t1 values (1,3);  

嘗試插入第二條記錄,這條記錄和session1插入的uk是沖突的,pk不沖突,是以先插入pk成功,然後檢查uk上的duplicate key加鎖:mode =3,但是session1已經在uk為1的記錄上加x鎖了,是以session 2進入鎖等待

加鎖堆棧:

3. 開啟purge操作:set global innodb_purge_run_now=on;

背景purge線程會去清理标記删除的二級索引記錄,但之前session1和session2都有請求uk上的排他鎖,記錄沒有了,這些鎖對象也需要做對應的處理,理論上鎖請求應該被下一條記錄鎖繼承,并轉換成gap鎖

我們來看看鎖繼承的邏輯,函數為lock_rec_inherit_to_gap:

對于如下場景,不會做鎖繼承:

a. 鎖類型為插入意向鎖

b. srv_locks_unsafe_for_binlog打開且鎖類型為x鎖

c. 鎖對應事務的隔離級别小于等于rc且鎖類型為x鎖

由于在執行類似replace, load datafile replace, insert on duplicate key update等操作時,當檢查duplicate key時加的是x鎖,本測試樣例的隔離級别為rc,是以鎖對象未被繼承,而是直接解除了,随後等待鎖的線程(session2)被喚醒(lock_update_delete –> lock_rec_reset_and_release_wait)。

從is表也可以看出來這一變化:

此時innodb_locks裡面已經沒有記錄了,purge操作“悄悄”的破壞了innodb的鎖協定。

随後喚醒session2繼續操作。

4. session 2: 由于獲得了記錄鎖,是以可以繼續插入記錄

5. session 1:由于已經完成了duplicate key檢查,是以可以繼續插入記錄

問題及解決

問題的原因alexey其實在bug上已經解釋的很清楚,innodb認為隻可能加s鎖來維持一緻性限制,是以當記錄被實體删除時,隻有s類型的鎖才被繼承。但對于replace這樣的操作,加的是x類型的鎖,這種鎖類型必須也要考慮進去,将其繼承給下一條記錄。alexey已經将patch push到percona server,改動也就一行:

[MySQL bug] unique key corruption again…..

ref:

繼續閱讀