MySQL間隙鎖問題


  間隙鎖(Gap Lock):鎖加在不存在的空閑空間,可以是兩個索引記錄之間,也可能是第一個索引記錄之前或最后一個索引之后的空間。

  最近用戶反饋說系統老是出現insert時,等待超時了,最后發現是insert間隙鎖!間隙鎖是innodb中行鎖的一種, 但是這種鎖鎖住的卻不止一行數據,他鎖住的是多行,是一個數據范圍。間隙鎖的主要作用是為了防止出現幻讀,但是它會把鎖定范圍擴大,有時候也會給我們帶來麻煩,我們就遇到了。 在數據庫參數中, 控制間隙鎖的參數是:

  innodb_locks_unsafe_for_binlog,這個參數默認值是OFF, 也就是啟用間隙鎖, 他是一個bool值, 當值為true時表示disable間隙鎖。

  那為了防止間隙鎖是不是直接將innodb_locaks_unsafe_for_binlog設置為true就可以了呢? 不一定!而且這個參數會影響到主從復制及災難恢復, 這個方法還尚待商量。

  間隙鎖的出現主要集中在同一個事務中先delete后 insert的情況下, 當我們通過一個參數去刪除一條記錄的時候, 如果參數在數據庫中存在,那么這個時候產生的是普通行鎖,鎖住這個記錄, 然后刪除, 然后釋放鎖。如果這條記錄不存在,問題就來了, 數據庫會掃描索引,發現這個記錄不存在, 這個時候的delete語句獲取到的就是一個間隙鎖,然后數據庫會向左掃描掃到第一個比給定參數小的值,向右掃描掃描到第一個比給定參數大的值, 然后以此為界,構建一個區間, 鎖住整個區間內的數據, 一個特別容易出現死鎖的間隙鎖誕生了。

  舉個例子:
  表testLock,有兩個屬性id,和name.有如下數據。

 

  開啟一個會話: session 1

 

sql> set autocommit=0;                 ##取消自動提交
sql> delete from testLock where id =6’;

  在開啟一個會話: session 2

sql> set autocommit=0;##取消自動提交
sql> insert into testLock(id,name) values(‘6’,’hahaha’);

  在沒有並發,或是極少並發的情況下, 這樣會可能會正常執行,在Mysql中, 事務最終都是穿行執行, 但是在高並發的情況下, 執行的順序就極有可能發生改變, 變成下面這個樣子:

sql> delete from testLock where id =6’;
sql> insert into testLock(id,name) values(‘6’, ‘hahaha’);

  這個時候最后一條語句:insert into testLock(id,name) values(‘6’, ‘hahaha’); 執行時就會爆出死鎖錯誤。因為刪除id = 6這條記錄的時候,id為6之后的部分都被鎖住了, 他們都取得了這一個數據段的共享鎖, 所以在獲取這個數據段的排它鎖時出現死鎖。

  這種問題的解決辦法:通過修改數據庫的參數innodb_locaks_unsafe_for_binlog來取消間隙鎖從而達到避免這種情況的死鎖的方式尚待商量, 那就只有修改代碼邏輯, 存在才刪除,盡量不去刪除不存在的記錄。


免責聲明!

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



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