MySQL鎖:InnoDB行鎖須要避免的坑

 

前言

  換了工做以後,接近半年沒有發博客了(一直加班),emmmm.....今天好不容易有時間,記錄下工做中遇到的一些問題,接下來應該重拾知識點了。由於新公司工做中MySQL庫常常出現查詢慢,鎖等待,節點掛掉........等一系列問題。致使每一個程序員頭都很大,一味抱怨「爲何我就查一條數據這麼卡」,"我TM加了索引的啊,怎麼還怎麼慢"...........我想默默說的是,大部分MySQL出現鎖等待,查詢奇慢的狀況基本都是由於SQL寫的很差(有坑),或者數據表設計的不完善。對,不用想!這些全部的坑很大一部分都是本身形成的。那麼是什麼緣由形成的,大部分只是抱怨,而不去關注MySQL的一些細節問題,好比:MySQL行鎖的細節,什麼狀況下會使用表鎖等。因此今天先討論記錄下InnoDB特有的行鎖的一些細節,增強認識。html

  InnoDB不一樣於MyISAM最大的兩個特色就是:一是支持事務,二是支持行鎖;毋庸置疑,由於這兩個特性大部分都採用InnoDB引擎,其中的支持行鎖就是InnoDB適合多併發優點所在,可是行鎖的一些細節沒有深刻理解過的話,可能會形成必定的誤解,形成「看似命中索引,走行鎖,結果倒是表鎖,最終致使鎖等待狀況」。程序員

 


1、InnoDB行鎖的實現方式

  經過給索引上的索引項加鎖來實現的,也就意味着:只有經過索引條件檢索數據,InnoDB才使用行級鎖,不然,InnoDB將使用表鎖。這一點在實際應用中特別須要注意,否則的話可能致使大量的鎖衝突,從而影響引起併發性能數據庫

  實驗一:對沒有索引的加鎖,致使表鎖

   1)準備工做:建tab_no_index表,表中無任何索引,並插入數據併發

    

 

   2)Session_1: 咱們給id=1的行加上排它鎖(for update),因爲id沒有索引,其實是表級鎖post

    

   3)Session_2:咱們給id=2的行加上排它鎖(for update),因爲id沒有索引,因此去申請表級鎖,可是卻出現了鎖等待!原因就是在沒有索引的狀況下,InnoDB只能使用表鎖性能

    

    備註:MySQL中的for update 僅適用於InnoDB(由於是隻有此引擎纔有行級鎖),而且必須開啓事務,在begin與commit之間才生效。for update是在數據庫中上鎖用的,能夠爲數據庫中的行上一個排它鎖。當一個事務的操做未完成時候,其餘事務能夠對這行讀取可是不能寫入或更新,只能等該事務Rollback, Commit, Lost connection…優化

   實驗二:對有索引的鍵值加鎖,會對全部涉及到的數據行加鎖

    1)準備工做:對id建索引以下url

     

    2)Session_1:此時id是有索引的,咱們對id=1 and name=1的一行加排它鎖;spa

    

    3)Session_2:訪問不一樣於Session_1的id=1, name=5行,可是索引鍵值是同樣的,照樣等待鎖,鎖衝突了。設計

    

   實驗三:多個索引時,不一樣的事務可使用不一樣的索引鎖定不一樣的行,不論什麼索引,InnoDB都會使用行鎖對數據加鎖(對有索引的行數據)

    1)準備工做:對tab_no_index追加name索引:alter table tab_no_index add index name(name);

    

    2)Session_1:開啓事務對id=1的行加排它鎖,即對name=1與name=5兩個數據加鎖。

    

    3)Session_2:開啓事務對name=2行加鎖,由於該數據沒有被加鎖,索引能夠得到鎖

    

    4)Session_3:再對name=5的數據進行加鎖,因爲該數據記錄已被Session_1鎖定,因此等待得到鎖。

   

  注意事項:即使使用了索引,但仍是要看MySQL具體對SQL的執行計劃,不必定能使用到

    如咱們對實驗三對name='2'進行加鎖,誤覺得name是int類型,原本name是有索引的,可是最後結果致使表鎖:

    

  

  具體請看MySQL的索引狀況。具體能夠參考以前個人一篇博文MySQL優化(1)--------經常使用的優化步驟MySQL優化(2)--------經常使用優化

 

2、間隙鎖(Next-Key鎖)

  當用範圍條件而不是相等條件檢索數據,並請求共享或者排它鎖的時候,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於不在範圍內的但並不存在的記錄,叫作「間隙(GAP)」,InnoDB也會對這個間隙加鎖,這就是所謂的間隙鎖

  如:select * from where id>100 for update 對id大於100的數據對加鎖,可是此時數據中id只有1,2….100,101,不只對存在的101的記錄加鎖,還會對大於101不存在的數據的間隙加鎖。

   此外,對使用相等條件請求給一個不存在的記錄加鎖,InnoDB也會使用間隙鎖,以下:

  Session_1:對不存在的id=6的記錄加鎖

  

 

  Session_2:插入id=6的記錄,也會出現鎖等待

  

 

3、何時使用表鎖?

  對於InnoDB表,在絕大部分狀況下都應該使用行鎖,由於事務和行鎖每每是咱們之因此選擇InnoDB表的理由,但在個別狀況下也使用表級鎖;

  1)事務須要更新大部分或所有數據,表又比較大,若是使用默認的行鎖,不只這個事務執行效率低,並且可能形成其餘事務長時間等待和鎖衝突;

  2)事務涉及多個表,比較複雜,極可能引發死鎖,形成大量事務回滾。

使用表鎖須要注意幾點:

  1)使用LOCK TABLES雖然能夠給InnoDB加表級鎖,表級鎖不是InnoDB存儲引擎層管理的,而是由其上一層MySQL Server負責的

  2)在用LOCK TABLES對InnoDB表加鎖時須要注意,要將Autocommit設置爲0,不然MySQL不會給表加鎖;事務結束前,不要用UNLOCK TABLES釋放表鎖,由於UNLOCK_TABLES隱含提交事務;COMMIT或ROLLBACK並不能釋放用LOCK TABLES加表級鎖。

   SET AUTOCOMMIT=0;

   LOCK TABLES table1 WRITE, table2 READ,...;

   [do something....]

   COMMIT;

   UNLOCK TABLES;

 

 


 

總結:

  • 從設計之初,就應該創建良好的索引機制,避免對關鍵字段搜索時形成表鎖;

  • 避免長時間事務未提交等狀況,致使鎖衝突,死鎖等狀況

  • 不要總是抱怨數據庫有問題,應該從自身寫的SQL分析出發,學會分析(數據庫不行大部分是由於SQL寫的有問題,沒錯,是自身問題)

  • 不要老是以爲這是DBA該作的事,開發者應該學會基本的SQL常識(如MySQL的最左索引,回表,索引覆蓋等知識),學會基本的優化步驟。

主要參考資料:《深刻淺出MySQL》(有須要PDF電子書的夥伴能夠評論或者私信我)

相關文章
相關標籤/搜索