InnoDB的鎖
InnoDB的行鎖:共享鎖、排他鎖、MDL鎖
共享鎖:又稱讀鎖、S鎖。一個事務獲取一個數據行的共享鎖,其他事務能獲取該行對應的共享鎖,但不能獲得排他鎖;即一個事務在讀取一個數據行時,其他事務也可以讀,但不能對數據進行增刪改查。
應用:
1.自動提交模式下的select查詢,不加任何鎖,直接返回查詢結果
2.通過select……lock in share mode在被讀取的行記錄或范圍上加一個讀鎖,其他事務可以讀,但是申請加寫鎖會被阻塞
排他鎖:又稱寫鎖、X鎖。一個事務獲取了一個數據行的寫鎖,其他事務就不能再獲取該行的其他鎖,寫鎖優先級最高。
應用:
1.一些DML操作會對行記錄加寫鎖
2.select for update會對讀取的行記錄上加一個寫鎖,其他任何事務都不能對鎖定的行加任何鎖,否則會被阻塞
MDL鎖:MySQL5.5引入,用于保證表中元數據的信息。在會話A中,表開啟了查詢事務后,會自動獲得一個MDL鎖,會話B就不能執行任何DDL語句的操作
行鎖實現方式
InnoDB 行鎖是通過給索引上的索引項加鎖來實現的,這一點 MySQL 與 Oracle 不同,后者是
通過在數據塊中對相應數據行加鎖來實現的。InnoDB 這種行鎖實現特點意味著:只有通過 索引條件檢索數據,InnoDB
才使用行級鎖,否則,InnoDB 將使用表鎖! 在實際應用中,要特別注意 InnoDB 行鎖的這一特性,不然的話,可能導致大量的鎖沖突,
從而影響并發性能。
行鎖的三種算法
InnoDB 存儲引擎有三種行鎖的算法,其分別是:
- Record Lock: 單個行記錄上的鎖
- Gap Lock: 間隙鎖,鎖定一個范圍,但不包含記錄本身
- Next-Key 鎖: Gap Lock + Record Lock,鎖定一個范圍,并且會鎖定記錄本身
RC模式下只采用Record Lock,RR模式下采用了Next-Key
加鎖場景分析
如果我們加鎖的行上存在主鍵索引,那么就會在這個主鍵索引上添加一個 Record Lock。
如果我們加鎖的行上存在輔助索引,那么我們就會在這行的輔助索引上添加 Next-Key Lock,并在這行之后的輔助索引上添加一個 Gap Lock
輔助索引上的 Next-Key Lock 和 Gap Lock 都是針對 Repeatable Read 隔離模式存在的,這兩種鎖都是為了防止幻讀現象的發生。
這里有一個特殊情況,如果輔助索引是唯一索引的話,MySQL 會將 Next-Key Lock 降級為 Record Lock,只會鎖定當前記錄的輔助索引。
如果唯一索引由多個列組成的,而我們只鎖定其中一個列的話,那么此時并不會進行鎖降級,還會添加 Next-Key Lock 和 Gap Lock。
在 InnoDB 存儲引擎中,對于 Insert 的操作,其會檢查插入記錄的下一條記錄是否被鎖定,若已經被鎖定,則不允許查詢。
意向鎖
意向鎖可以分為意向共享鎖(Intention Shared Lock, IS)和意向排他鎖(Intention eXclusive
Lock,
IX)。但它的鎖定方式和共享鎖和排他鎖并不相同,意向鎖上鎖只是表示一種“意向”,并不會真的將對象鎖住,讓其他事物無法修改或訪問。例如事物T1想要修改表test
中的行r1
,它會上兩個鎖:
- 在表
test
上意向排他鎖 - 在行
r1
上排他鎖
事物T1在test
表上上了意向排他鎖,并不代表其他事物無法訪問test
了,它上的鎖只是表明一種意向,它將會在db
中的test
表中的某幾行記錄上上一個排他鎖。
|
意向共享鎖 |
意向排他鎖 |
共享鎖 |
排他鎖 |
意向共享鎖 |
兼容 |
兼容 |
兼容 |
不兼容 |
意向排他鎖 |
兼容 |
兼容 |
不兼容 |
不兼容 |
共享鎖 |
兼容 |
不兼容 |
兼容 |
不兼容 |
排他鎖 |
不兼容 |
不兼容 |
不兼容 |
不兼容 |
一致性非鎖定讀
一致性非鎖定讀是指 InnoDB 存儲引擎通過行多版本控制(multi
version)的方式來讀取當前執行時間數據庫中行的數據。具體來說就是如果一個事務讀取的行正在被鎖定,那么它就會去讀取這行數據之前的快照數據,而不會等待這行數據上的鎖釋放。這個讀取流程如圖1所示:

行的快照數據是通過undo段來實現的,而undo段用來回滾事務,所以快照數據本身沒有額外的開銷。此外,讀取快照數據時不需要上鎖的,因為沒有事務會對快照數據進行更改。
MySQL 中并不是每種隔離級別都采用非一致性非鎖定讀的讀取模式,而且就算是采用了一致性非鎖定讀,不同隔離級別的表現也不相同。在 READ
COMMITTED 和 REPEATABLE READ 這兩種隔離級別下,InnoDB存儲引擎都使用一致性非鎖定讀。但是對于快照數據,READ
COMMITTED 隔離模式中的事務讀取的是當前行最新的快照數據,而 REPEATABLE READ
隔離模式中的事務讀取的是事務開始時的行數據版本。
一致性鎖定讀
在 InnoDB 存儲引擎中,select
語句默認采取的是一致性非鎖定讀的情況,但是有時候我們也有需求需要對某一行記錄進行鎖定再來讀取,這就是一致性鎖定讀。
InnoDB 對于select
語句支持以下兩種鎖定讀:
select ... for update
select ... lock in share mode
select ... for update
會對讀取的記錄加一個X鎖,其他事務不能夠再來為這些記錄加鎖。select ... lock in share mode
會對讀取的記錄加一個S鎖,其它事務能夠再為這些記錄加一個S鎖,但不能加X鎖。
對于一致性非鎖定讀,即使行記錄上加了X鎖,它也是能夠讀取的,因為它讀取的是行記錄的快照數據,并沒有讀取行記錄本身。
select ... for update
和select ... lock in share mode
這兩個語句必須在一個事務中,當事務提交了,鎖也就釋放了。因此在使用這兩條語句之前必須先執行begin
, start transaction
,或者執行set autocommit = 0
。
InnoDB 在不同隔離級別下的一致性讀及鎖的差異
consisten read //一致性讀
share locks //共享鎖
Exclusive locks //排他鎖
|
|
讀未提交 |
讀已提交 |
可重復讀 |
串行化 |
SQL |
條件 |
|
|
|
|
select |
相等 |
None locks |
Consisten read/None lock |
Consisten read/None lock |
Share locks |
|
范圍 |
None locks |
Consisten read/None lock |
Consisten read/None lock |
Share Next-Key |
update |
相等 |
Exclusive locks |
Exclusive locks |
Exclusive locks |
Exclusive locks |
|
范圍 |
Exclusive next-key |
Exclusive next-key |
Exclusive next-key |
Exclusive next-key |
Insert |
N/A |
Exclusive locks |
Exclusive locks |
Exclusive locks |
Exclusive locks |
Replace |
無鍵沖突 |
Exclusive locks |
Exclusive locks |
Exclusive locks |
Exclusive locks |
|
鍵沖突 |
Exclusive next-key |
Exclusive next-key |
Exclusive next-key |
Exclusive next-key |
delete |
相等 |
Exclusive locks |
Exclusive locks |
Exclusive locks |
Exclusive locks |
|
范圍 |
Exclusive next-key |
Exclusive next-key |
Exclusive next-key |
Exclusive next-key |
Select … from … Lock in share mode |
相等 |
Share locks |
Share locks |
Share locks |
Share locks |
|
范圍 |
Share locks |
Share locks |
Exclusive next-key |
Exclusive next-key |
Select * from … For update |
相等 |
Exclusive locks |
Exclusive locks |
Exclusive locks |
Exclusive locks |
|
范圍 |
Exclusive locks |
Exclusive locks |
Exclusive next-key |
Exclusive next-key |
Insert into … Select … |
innodb_locks_ unsafe_for_bi nlog=off |
Share Next-Key |
Share Next-Key |
Share Next-Key |
Share Next-Key |
(指源表鎖) |
innodb_locks_ unsafe_for_bi nlog=on |
None locks |
Consisten read/None lock |
Consisten read/None lock |
Share Next-Key |
create table … Select … |
innodb_locks_ unsafe_for_bi nlog=off |
Share Next-Key |
Share Next-Key |
Share Next-Key |
Share Next-Key |
(指源表鎖) |
innodb_locks_ unsafe_for_bi nlog=on |
None locks |
Consisten read/None lock |
Consisten read/None lock |
Share Next-Key |
在了解 InnoDB 鎖特性后,用戶可以通過設計和 SQL 調整等措施減少鎖沖突和死鎖,包括:
- 盡量使用較低的隔離級別;
- 精心設計索引,并盡量使用索引訪問數據,使加鎖更精確,從而減少鎖沖突的機會;
- 選擇合理的事務大小, 小事務發生鎖沖突的幾率也更小;
- 給記錄集顯示加鎖時,最好一次性請求足夠級別的鎖。比如要修改數據的話,最好直接申請排他鎖,而不是先申請共享鎖,修改時再請求排他鎖,這樣容易產生死鎖;
- 不同的程序訪問一組表時,應盡量約定以相同的順序訪問各表,對一個表而言,盡可能以固定的順序存取表中的行。這樣可以大大減少死鎖的機會;
- 盡量用相等條件訪問數據,這樣可以避免間隙鎖對并發插入的影響;
- 不要申請超過實際需要的鎖級別;除非必須,查詢時不要顯示加鎖;
- 對于一些特定的事務,可以使用表鎖來提高處理速度或減少死鎖的可能。
參考資料
1.https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-intention-locks mysql官網開發手冊
2.《MySQL 技術內幕 – InnoDB 存儲引擎》
3.《深入淺出MySQL》
4.https://www.modb.pro/db/33873