【MySQL】鎖之InnoDB

紙上得來終覺淺,絕知此事要躬行。mysql

InnoDB行鎖

開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。sql

InnoDB 實現瞭如下兩種類型的行鎖。併發

  • 共享鎖(S):又稱爲讀鎖,簡稱S鎖,共享鎖就是多個事務對於同一數據能夠共享一把鎖,都能訪問到數據,可是隻能讀不能修改。
  • 排他鎖(X):又稱爲寫鎖,簡稱X鎖,排他鎖就是不能與其餘鎖並存,如一個事務獲取了一個數據行的排他鎖,其餘事務就不能再獲取該行的其餘鎖(包括共享鎖和排他鎖),可是獲取排他鎖的事務是能夠對數據就行讀取和修改。

能夠經過如下語句顯示給記錄集加共享鎖或排他鎖 。性能

共享鎖(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE

排他鎖(X) :SELECT * FROM table_name WHERE ... FOR UPDATE

對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X);
對於普通SELECT語句,InnoDB不會加任何鎖;測試

行鎖案例

在測試前仍然準備相關測試表和數據,注意表的存儲引擎爲InnoDB:優化

create table test_innodb_lock(
	id int(11),
	name varchar(16),
	sex varchar(1)
)engine = innodb default charset=utf8;

insert into test_innodb_lock values(11,'100','1');
insert into test_innodb_lock values(3,'3','1');
insert into test_innodb_lock values(4,'400','0');
insert into test_innodb_lock values(5,'500','1');
insert into test_innodb_lock values(6,'600','0');
insert into test_innodb_lock values(7,'700','0');
insert into test_innodb_lock values(8,'800','1');
insert into test_innodb_lock values(9,'900','1');
insert into test_innodb_lock values(1,'200','0');

create index idx_test_innodb_lock_id on test_innodb_lock(id);
create index idx_test_innodb_lock_name on test_innodb_lock(name);

建立完成,咱們看一下表結構和表數據,方便後面操做查看:設計

mysql> desc test_innodb_lock;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | YES  | MUL | NULL    |       |
| name  | varchar(16) | YES  | MUL | NULL    |       |
| sex   | varchar(1)  | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)


mysql> select * from test_innodb_lock;
+------+------+------+
| id   | name | sex  |
+------+------+------+
|   11 | 100  | 1    |
|    3 | 3    | 1    |
|    4 | 400  | 0    |
|    5 | 500  | 1    |
|    6 | 600  | 0    |
|    7 | 700  | 0    |
|    8 | 800  | 1    |
|    9 | 900  | 1    |
|    1 | 200  | 0    |
+------+------+------+
9 rows in set (0.00 sec)

行鎖基本演示

Session-1 Session-2
關閉自動提交功能 關閉自動提交功能
能夠正常的查詢出所有的數據 能夠正常的查詢出所有的數據
查詢 id 爲 3 的數據 查詢 id 爲 3 的數據
更新 id 爲 3 的數據,可是不提交 更新id爲3 的數據, 處於等待狀態
經過 commit,提交事務 解除阻塞,更新正常進行
以上, 操做的都是同一行的數據,接下來,演示不一樣行的數據
更新 id 爲 3 數據,正常的獲取到行鎖 , 執行更新 ; 因爲與Session-1 操做不是同一行,獲取當前行鎖,執行更新;

無索引行鎖升級爲表鎖

若是不經過索引條件檢索數據,那麼InnoDB將對錶中的全部記錄加鎖,實際效果跟表鎖同樣。3d

查看當前表的索引 :code

show  index  from test_innodb_lock ;

接下來咱們看一個例子,當索引失效的時候,行鎖升級爲表鎖的過程:blog

以上兩個客戶端對一行數據進行修改時,因爲執行更新時,name字段原本爲varchar類型, 咱們是做爲int類型使用,存在類型轉換,索引失效,最終行鎖變爲表鎖,致使Session-2更新數據處於等待狀態。

間隙鎖的危害

當咱們用範圍條件,而不是使用相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據進行加鎖; 對於鍵值在條件範圍內但並不存在的記錄,叫作 "間隙(GAP)" , InnoDB也會對這個 "間隙" 加鎖,這種鎖機制就是所謂的間隙鎖(Next-Key鎖)

下面是一個間隙鎖的案例:

Session-1 Session-2
關閉事務的自動提交 關閉事務的自動提交
執行更新語句 執行更新語句, 但處於阻塞狀態
提交事務 解除阻塞,執行更新成功
執行提交操做

因爲Session-1的插入操做是一個範圍,咱們表中存在id不連續的狀況,致使在更新時不只會加排它鎖,還會在當前訪問增長間隙鎖,致使Session-2在插入元素時處於等待狀態。

InnoDB 行鎖爭用狀況

查看行鎖競爭狀況:

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存儲引擎因爲實現了行級鎖定,雖然在鎖定機制的實現方面帶來了性能損耗可能比表鎖會更高一些,可是在總體併發處理能力較高。可是,InnoDB的行級鎖也是一把雙刃劍,當咱們使用不當的時候,可能會讓InnoDB的總體性能降低。

優化建議:

  • 儘量經過索引來完成數據操做,避免無索引行鎖升級爲表鎖。
  • 合理設計索引,儘可能縮小鎖的範圍
  • 儘量減小索引條件,及索引範圍,避免間隙鎖
  • 儘可能控制事務大小,減小鎖定資源量和時間長度
  • 儘可以使用低級別事務隔離(可是須要業務層面知足需求)
相關文章
相關標籤/搜索