天天看點

從一個案例 複習MySQL加鎖機制

一前言

該文源自于和一個DBA 同行 @邱神醫 (集資料庫技術和醫學知識于一身的DBA)的技術讨論。

RC級别:

create table t1(id int primary key, name varchar(30));
insert into t1 values(1, 'a'),(4, 'c'),
(7, 'b'),(10, 'a'),(20, 'd'),(30, 'b');
commit;

           

複制

案例一

--sess1           --sess2
begin;                              
delete from t1
where id = 10;
                 begin;
                 delete from t1 
                 where id < 9; ## 被阻塞

           

複制

如果會話A,會話B的執行順序調整一下,會話B 則不會被阻塞。

案例二

--sess1           --sess2
begin;                              
delete from t1
where id < 9;
                  begin;
                  delete from t1 
                  where id = 10;#不會阻塞

           

複制

案例三 sess2 使用 delete from t1 where id < 9 order by id desc; 則沒有阻塞。

--sess1          --sess2
begin;                              
delete from t1
where id = 10;
                 begin;
                 delete from t1 
                 where id < 9 order by id desc; ## 沒有阻塞

           

複制

為什麼呢?

二 基礎知識

sql被block住說明存在鎖等待/鎖沖突,等待其他會話釋放鎖。其實問題的核心在于 加鎖順序和加鎖範圍。這裡結合 丁奇 《MySQL 實戰45講》中的講述的 加鎖方式(2個原則2個優化1個bug)

原則 1:加鎖的基本機關是 next-key lock。

原則 2:查找過程中通路到的對象才會加鎖。

優化 1:索引上的等值查詢,給唯一索引加鎖的時候,next-key lock 退化為行鎖。

優化 2:索引上的等值查詢,向右周遊時且最後一個值不滿足等值條件的時候,next-key lock 退化為間隙鎖。

一個 bug:唯一索引上的範圍查詢會通路到不滿足條件的第一個值為止。

在讀送出隔離級别下還有一個優化,即:語句執行過程中加上的行鎖,在語句執行完成後,就要把“不滿足條件的行”上的行鎖直接釋放了,不需要等到事務送出

三 案例分析

案例一

sess1 持有id = 10 該記錄的行鎖

lock_mode X locks rec but not gap

sess2 where 條件是 id<9 ,因為id=9 的記錄不存在,根據原則 一個bug 故需要申請第一個不滿足條件的記錄id=10 的next-key ,也就是(7,10],此時id=10 的行鎖被sess1 持有,故sess2産生鎖等待被block住。

案例二

sess1 先執行delete t1 where id<9 ,根據RC模式的加鎖方式 ,雖然要擷取到第一個不滿足記錄的id=10 的next-key lock ,但是因為id=10 不符合 id<9 的條件,會釋放鎖,最終降級為 (7,10) 之間的gap lock。

再解釋一下:這個過濾操作是 MySQL Sever層做的,也就是 innodb層把包括id=10的記錄加鎖然後發送給MySQL Server層,然後 MySQL Sever層判斷是否where條件可以結束了,可以結束了則将不滿足條件的id=10解鎖。

sess2 delete where id=10 和 sess1 持有的gap lock不沖突,故可以順利執行。

案例三

sess1 持有id = 10 該記錄的行鎖

lock_mode X locks rec but not gap

sess2 where 條件是 id<9 order by id desc ,通過innodb api接口通路資料的時候從擷取到第一個滿足條件的記錄是id=7,不會通路 id=10這條記錄。故也不會加上id=10 的next-key lock. 不會與sess1持有的鎖沖突。

小結

經過這個幾個案例又複習了一次 MySQL的 加鎖機制,同時 也牆裂 安利 丁奇的課程,不管是新人還是老司機 ,都值得一讀。