mysql間隙鎖


什么是間隙鎖(gap lock)?

  間隙鎖是一個在索引記錄之間的間隙上的鎖。

間隙鎖的作用?  

  保證某個間隙內的數據在鎖定情況下不會發生任何變化。比如我mysql默認隔離級別下的可重復讀(RR)。

  當使用唯一索引來搜索唯一行的語句時,不需要間隙鎖定。如下面語句的id列有唯一索引,此時只會對id值為10的行使用記錄鎖。

    select * from t where id = 10 for update;// 注意:普通查詢是快照讀,不需要加鎖
  如果,上面語句中id列沒有建立索引或者是非唯一索引時,則語句會產生間隙鎖。

  如果,搜索條件里有多個查詢條件(即使每個列都有唯一索引),也是會有間隙鎖的。

  需要注意的是,當id列上沒有索引時,SQL會走聚簇索引的全表掃描進行過濾,由於過濾是在MySQL Server層面進行的。因此每條記錄(無論是否滿足條件)都會被加上X鎖。但是,為了效率考量,MySQL做了優化,對於不滿足條件的記錄,會在判斷后放                鎖,最終持有的,是滿足條件的記錄上的鎖。但是不滿足條件的記錄上的加鎖/放鎖動作是不會省略的。所以在沒有索引時,不滿足條件的數據行會有加鎖又放鎖的耗時過程。

間隙的范圍?

  根據檢索條件向下尋找最靠近檢索條件的記錄值A作為左區間,向上尋找最靠近檢索條件的記錄值B作為右區間,即鎖定的間隙為(A,B] 左開右閉

通過上面的描述,感覺很抽象?

  沒關系,下面我們來通過具體的實驗來說明gap鎖。再次強調(當使用唯一索引來搜索唯一行的語句時,不需要間隙鎖定

  我們先創建一張數據表: 

MariaDB [locktest]> create table gaplockt(
-> id int not null,
-> name varchar(255) not null primary key,
-> key `id_index` (`id`)
-> );

  在向表中插入一些數據:

MariaDB [locktest]> insert into gaplockt values(1,'panchao'),(5,'songzuer'),(10,
'yangmi');

  現在我們的表情況是這樣的:

MariaDB [locktest]> select * from gaplockt;
+----+----------+
| id | name |
+----+----------+
| 1 | panchao |
| 5 | songzuer |
| 10 | yangmi |
+----+----------+

  接下來我們設置autocommit = 0;

MariaDB [locktest]> set autocommit = 0;

MariaDB [locktest]> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 0 |
+--------------+

  現在我們的准備工作已經做完了,我們再事物A中給記錄添加一個寫鎖:

MariaDB [locktest]> select * from gaplockt where id = 5 for update;
+----+----------+
| id | name |
+----+----------+
| 5 | songzuer |
+----+----------+

  由於id不是唯一索引,表上加上相應的gap鎖。如下圖:

  以上語句會給表加上的gap鎖包括(gap2和gap3),也就是1~5U5~10,特別注意這個區間為左開右閉區間(1,10],沒理解沒關系,我們接下來通過實驗來驗證。

  實驗1、

  MariaDB [locktest]> insert into gaplockt values(6,'jingruyang');

  ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 

  MariaDB [locktest]> insert into gaplockt values(4,'jingruyang');
  ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

  MariaDB [locktest]> insert into gaplockt values(1,'jingruyang');
  Query OK, 1 row affected (0.00 sec)

  MariaDB [locktest]> insert into gaplockt values(10,'jingruyang10');

  ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

  通過以上實驗,我們可以看到當id = 1的時候,我們是能添加數據的,但是當id = 10的時候我們是不能添加數據的,事實證明,我們的這個gap鎖區間為(1,10]

  實驗2、

  接下來我們來討論gap1和gap4的問題。

  當我們執行以下語句的時候,或收獲gap4鎖。

  MariaDB [locktest]> select * from gaplockt where id = 100 for update;

  那么gap4鎖的范圍是多少呢? 是(10,100]嗎?

  直接告訴你們答案,不是,它的范圍是(10,+∞),下面我們通過實驗來驗證。

  MariaDB [locktest]> insert into gaplockt values(99,'jingruyang99');
  ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
  MariaDB [locktest]> insert into gaplockt values(100,'jingruyang100');
  ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
  MariaDB [locktest]> insert into gaplockt values(1000,'jingruyang1000');
  ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

  可以看到id=99   id= 100  id=1000  都是上了鎖的,所以gap4的區間為(10,+∞)

  我們再來討論gap1

  執行以下語句獲取gap1

  MariaDB [locktest]> select * from gaplockt where id = -100 for update;

  gap1的區間我直接告訴大家,(-∞,1)

  下面我們通過實驗證明。

  MariaDB [locktest]> insert into gaplockt values(-99,'jingruyang99');
  ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
  MariaDB [locktest]> insert into gaplockt values(-100,'jingruyang99');
  ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
  MariaDB [locktest]> insert into gaplockt values(-10000,'jingruyang99');
  ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

  看到上面的結果,就不用我多說什么了吧。

以下是兩個比較有參考價值的鏈接,有興趣的朋友可以看一下。

  Mysql中的GAP鎖(間隙鎖)

  MySQL的innoDB鎖機制以及死鎖處理

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM