前言:前面學習了表鎖的相關知識,本篇主要介紹行鎖的相關知識。行鎖偏向InnoDB存儲引擎,開銷大,加鎖慢,會出現死鎖,鎖定粒度小,發生鎖沖突的概率低,但並發度高。
0.准備
#1.創建相關測試表tb_innodb_lock,注意數據庫引擎為InnoDB。
drop table if exists test_innodb_lock; CREATE TABLE test_innodb_lock ( a INT (11), b VARCHAR (20) ) ENGINE INNODB DEFAULT charset = utf8; insert into test_innodb_lock values (1,'a'); insert into test_innodb_lock values (2,'b'); insert into test_innodb_lock values (3,'c'); insert into test_innodb_lock values (4,'d'); insert into test_innodb_lock values (5,'e');
#2.創建索引。
create index idx_lock_a on test_innodb_lock(a); create index idx_lock_b on test_innodb_lock(b);
1.行鎖定基本演示
#1.打開A、B另個會話,並關閉數據庫的自動提交。
set autocommit=0;
#2.在A會話中做更新操作。

從查詢結果可知:在A會話中更新數據成功。
#3.在B會話中做查詢操作。

分析:
B會話中並沒有讀取到A會話中更新后的值。(讀己知所寫:自己更改的數據自己知道,但是如果未提交,其他人是不知道的。)
#4.在A會話中執行commit命令,然后在B會話中再次查詢。

#5.在A會話中做更新操作,然后在B會話中也做更新操作。


這時在A會話中commit操作,可看到B會話中發生了更新操作。

分析:
因為我們操作的同一行數據,而由於InnoDB為行鎖,在A會話未提交時,B會話只有阻塞等待。如果操作不同行,則不會出現阻塞情況。
2.索引失效導致行鎖升級為表鎖
#1.在A會話中執行如下更新語句。

#2.在B會話中執行如下更新語句。

分析:
首先將表中的數據更新為b=1000,2000,3000,4000,5000。
在A會話中操作的第一行數據,但是where中使用了b=1000,發生了自動轉換導致索引失效,從而使鎖的級別從行鎖升級為表鎖,因此B會話中操作第五行數據出現阻塞的情況。
3.間隙鎖的危害
#1.間隙鎖定義:
當我們用范圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖,對於鍵值在條件范圍內但不存在的記錄,叫作“間隙(GAP)”。
InnoDB也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖。(Next-Key鎖)
#2.間隙鎖危害:
因為在Query執行過程中通過范圍查找的話,會鎖定整個范圍內的所有索引鍵值,即使這個索引不存在。間隙鎖有一個比較致命的弱點,就是當鎖定一個范圍鍵值后,即使某些不存在的鍵值也會被無辜的鎖定,而造成在鎖定的時候無法插入鎖定值范圍內的任何數據。在某些場景下這個可能會對性能造成很大的危害。
#3.間隙鎖演示。
要演示間隙鎖,我們需要對test_innodb_lock中的數據進行修改,修改后的數據如下:

注:a列的值並不連續。
①在A會話中執行如下語句。

②在B會話中執行如下語句。

只有在A會話commit后,B會話才能進行插入操作。
4.如何鎖定某一行
利用for update。
①在A會話中執行如下語句。

此時就鎖定了a=7這一行。
②在B會話中對該行進行更新操作。

只有在A會話中進行了commit后,B會話的更新操作才能執行。

5.行鎖分析
#1.使用如下命令。
show status like 'innodb_row_lock%';

各個狀態量說明:
①Innodb_row_lock_current_waits:當前正在等待鎖定的數量。
②Innodb_row_lock_time:從系統啟動到現在鎖定的時長。
③Innodb_row_lock_time_avg:每次等待鎖所花平均時間。
④Innodb_row_lock_time_max:從系統啟動到現在鎖等待最長的一次所花的時間。
⑤Innodb_row_lock_waits:系統啟動后到現在總共等待鎖的次數。
這個五個狀態量中,比較重要的是:
Innodb_row_lock_time、Innodb_row_lock_time_avg和Innodb_row_lock_waits。尤其是等待次數很高,而且每次等待時長不小時,就需要分析優化了。
6.優化建議
①盡可能讓所有數據都通過索引來完成,避免無索引行升級為表鎖。
②合理設計索引,盡量縮小鎖的范圍。
③盡可能使用較少的檢索條件,避免間隙鎖。
④盡量控制事務大小,減少鎖定資源量和時間長度。
⑤盡可能降低事務隔離級別。
7.頁鎖
開銷和加鎖時間介於表鎖和行鎖之間,會出現死鎖,鎖定粒度介於表鎖和行鎖之間,並發度一般(了解即可)。
總結
①InnoDB存儲引擎由於實現了行級鎖定,雖然在鎖定機制的實現方面所帶來的性能損耗可能比表級鎖定會更高一些(多個鎖,一個鎖),但是在整體並發處理能力方面要遠遠優於MyISAM的表級鎖定。當系統處於高並發量的時候,InnoDB的整體性能和MyISAM相比就會有比較明顯的優勢了。
②InnoDB的行鎖定同樣尤其脆弱的一面(間隙鎖危害),當使用不當時可能會讓InnoDB的整體性能表現不僅不能比MyISAM高,甚至可能更差。
by Shawn Chen,2018.6.28日,下午。
