MySQL鎖 InnoDB行鎖需要避免的坑

2022-03-18 08:36:38 字數 3506 閱讀 9290

換了工作之後,接近半年沒有發部落格了(一直加班),emmmm.....今天好不容易有時間,記錄下工作中遇到的一些問題,接下來應該重拾知識點了。因為新公司工作中mysql庫經常出現查詢慢,鎖等待,節點掛掉........等一系列問題。導致每個程式設計師頭都很大,一味抱怨「為什麼我就查一條資料這麼卡」,"我tm加了索引的啊,怎麼還怎麼慢"...........我想默默說的是,大部分mysql出現鎖等待,查詢奇慢的情況基本都是因為sql寫的不好(有坑),或者資料表設計的不完善。對,不用想!這些所有的坑很大一部分都是自己造成的。那麼是什麼原因造成的,大部分只是抱怨,而不去關注mysql的一些細節問題,比如:mysql行鎖的細節,什麼情況下會使用表鎖等。所以今天先討論記錄下innodb特有的行鎖的一些細節,加強認識。

innodb不同於myisam最大的兩個特點就是:一是支援事務,二是支援行鎖;毋庸置疑,因為這兩個特性大部分都採用innodb引擎,其中的支援行鎖就是innodb適合多併發優勢所在,但是行鎖的一些細節沒有深入理解過的話,可能會造成一定的誤解,造成「看似命中索引,走行鎖,結果卻是表鎖,最終導致鎖等待情況」。

通過給索引上的索引項加鎖來實現的,也就意味著:只有通過索引條件檢索資料,innodb才使用行級鎖,否則,innodb將使用表鎖。這一點在實際應用中特別需要注意,不然的話可能導致大量的鎖衝突,從而影響引發併發效能

1)準備工作:建tab_no_index表,表中無任何索引,並插入資料

2)session_1: 我們給id=1的行加上排它鎖(for update),由於id沒有索引,實際上是表級鎖;

3)session_2:我們給id=2的行加上排它鎖(for update),由於id沒有索引,所以去申請表級鎖,但是卻出現了鎖等待!原因就是在沒有索引的情況下,innodb只能使用表鎖。

備註:mysql中的for update 僅適用於innodb(因為是只有此引擎才有行級鎖),並且必須開啟事務,在begin與commit之間才生效。for update是在資料庫中上鎖用的,可以為資料庫中的行上乙個排它鎖。當乙個事務的操作未完成時候,其他事務可以對這行讀取但是不能寫入或更新,只能等該事務rollback, commit, lost connection…

1)準備工作:對id建索引如下

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

3)session_2:訪問不同於session_1的id=1, name=5行,但是索引鍵值是一樣的,照樣等待鎖,鎖衝突了。

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鎖定,所以等待獲得鎖。

如我們對實驗三對name='2'進行加鎖,誤以為name是int型別,本來name是有索引的,但是最後結果導致表鎖:

具體請看mysql的索引情況。具體可以參考之前我的一篇博文mysql優化(1)--------常用的優化步驟、mysql優化(2)--------常用優化

當用範圍條件而不是相等條件檢索資料,並請求共享或者排它鎖的時候,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的記錄,也會出現鎖等待

對於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;

Innodb 行鎖與表鎖

行鎖與表鎖 innodb預設是行級別的鎖,當有明確指定的主鍵時候,是行級鎖。否則是表級別。for update的注意點 for update 僅適用於innodb,並且必須開啟事務,在begin與commit之間才生效。要測試for update的鎖表情況,可以利用mysql的command mod...

mysql中innodb引擎的行鎖

一 資料庫引擎 mysql常用的資料庫引擎也就是myisam和innodb兩種,相比較起myisam而言innodb支援了事務 外來鍵等功能,具有更好的併發性支援,所以在大併發的情況的下我們一般選擇的是innodb來作為我們的資料庫儲存引擎,而myisam相較innodb的執行效能上會有更好的表現,...

mysql 行鎖 訂票 mysql 行鎖

在電子商務裡,經常會出現庫存數量少,購買的人又特別多,大併發情況下如何確保商品數量不會被多次購買.其實很簡單,利用事務 for update就可以解決.我們都知道for update實際上是共享鎖,是可以被讀取的.但是如何在執行時,不被讀取呢.簡單來說 假設現在庫存為1,現在有a和b同時購買 先開啟...