前面一文 mysql鎖 介紹了mysql innodb存儲引擎的各種鎖,本文介紹一下innodb存儲引擎的間隙鎖,就以下問題展開討論
1.什么是間隙鎖?間隙鎖是怎樣產生的?
2.間隙鎖有什么作用?
3.使用間隙鎖有什么隱患?
一、間隙鎖的基本概念
1.什么叫間隙鎖
當我們用范圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於鍵值在條件范圍內但不存在的記錄,叫做“間隙(GAP)”,InnoDB也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖(NEXT-KEY)鎖。
2.間隙鎖的產生
上面的文字很抽象,現在舉個栗子,介紹間隙鎖是怎么產生的:
假設有以下表t_student:(其中id為PK,name為非唯一索引)
id | name | sex | address |
1 | zhaoyi | 0 | beijin |
3 | sunsan | 1 | shanghai |
4 | lisi | 0 | guangzhou |
5 | zhouwu | 0 | shenzhen |
6 | wuliu | 1 | hangzhou |
這個時候我們發出一條這樣的加鎖sql語句:
select id,name from t_student where id > 0 and id < 5 for update;
這時候,我們命中的數據為以下着色部分:
id | name | sex | address |
1 | zhaoyi | 0 | beijin |
3 | sunsan | 1 | shanghai |
4 | lisi | 0 | guangzhou |
5 | zhouwu | 0 | shenzhen |
6 | wuliu | 1 | hangzhou |
細心的朋友可能就會發現,這里缺少了條id為2的記錄,我們的重點就在這里。
select ... for update這條語句,是會對數據記錄加鎖的,這里因為命中了索引,加的是行鎖。從數據記錄來看,這里排它鎖鎖住數據是id為1、3和4的這3條數據。
但是,看看前面我們的介紹——對於鍵值在條件范圍內但不存在的記錄,叫做“間隙(GAP)”,InnoDB也會對這個“間隙”加鎖。
好了,我們這里,鍵值在條件范圍但是不存在的記錄,就是id為2的記錄,這里會對id為2數據加上間隙鎖。假設這時候如果有id=2的記錄insert進來了,是要等到這個事務結束以后才會執行的
二、間隙鎖的作用
總的來說,有2個作用:防止幻讀和防止數據誤刪/改
1.防止幻讀
關於幻讀的概念可以參考我這篇文章 https://blog.csdn.net/mweibiao/article/details/80805031 ,這里就不多做解釋了
假設有下面場景
時間 | 事務A | 事務B |
T1 | select count(1) from t_student where id > 1; | |
T2 | insert into t_student values(2,'qianer',1,'nanjing'); | |
T3 | commit; | |
T4 | select count(1) from t_student where id > 1; | |
T5 | commit; |
如果沒有間隙鎖,事務A在T1和T4讀到的結果是不一樣的,有了間隙鎖,讀的就是一樣的了
2.防止數據誤刪/改
這個作用比較重要,假設以下場景:
時間 | 事務A | 事務B |
T1 | delete from t_student where id < 4; | |
T2 | insert into t_student values(2,'qianer',1,'nanjing'); | |
T3 | commit; | |
T4 | commit; |
這種情況下,如果沒有間隙鎖,會出現的問題是:id為2的記錄,剛加進去,就被刪除了,這種情況有時候對業務,是致命性的打擊。加了間隙鎖之后,由於insert語句要等待事務A執行完之后釋放鎖,避免了這種情況
三.使用間隙鎖的隱患
最大的隱患就是性能問題
前面提到,假設這時候如果有id=2的記錄insert進來了,是要等到這個事務結束以后才會執行的,假設是這種場景
時間 | 事務A | 事務B |
T1 | select * from t_student where id>1 and id < 100 for update; | |
T2 | insert into t_student values(2,'qianer',1,'nanjing'); | |
T3 | update t_student set xxxx where id=xxx; | |
T4 | update t_student set xxxx where id=xxx; | |
T5 | update t_student set xxxx where id=xxx; | |
T6 | … | |
T7 | commit; |
這種情況,對插入的性能就有很大影響了,必須等到事務結束才能進行插入,性能大打折扣
更有甚者,如果間隙鎖出現死鎖的情況下,會更隱晦,更難定位