InnoDB 爲了保證併發能力,採起行級加鎖策略。爲了實現事務的隔離級別,InnoDB 中又引入了各類不一樣的行級鎖機制。不一樣的加鎖順序、加鎖類型、鎖的多少以及影響範圍將直接影響到整個事務執行效率與執行時間直接影響 MySQL 的吞吐能力,不恰當的加鎖策略甚至有可能產生死鎖,所以咱們又必要對整個過程有所瞭解。html
InnoDB 採用了 B+ Tree 的數據結構與彙集索引的數據組織形式,索引在 InnoDB 引擎中佔據了很是重要的位置,InnoDB 加鎖過程就是對索引進行加鎖的一個過程。在分析 InnoDB 加鎖以前,咱們須要知道 InnoDB 加鎖是和什麼有關,這一點很是重要。對於不一樣的事務隔離離別、不一樣的列 InnoDB 採起的策略與使用的鎖的類型都不同,影響加鎖的因素有以下兩種:mysql
select ... from
這類語句而言,因爲 InnoDB 採起了一致性讀策略,通常是不會加鎖的,可是在Serialzable
級別,InnoDB 會對搜索過程當中遇到的二級索引加共享臨鍵鎖
。對於Read Committed
級別不會採起間隙鎖
的加鎖策略。update ... from ...
語句在Read Repeatable
級別下使用了排他臨鍵鎖
,而在Read Committed
級別下使用的是排他行鎖
。對於 InnoDB 而言,雖然加鎖的類別繁多,加鎖形式也靈活多樣,但也遵循了一些原則:sql
select ... from ...
語句,使用快照讀,通常狀況下不加鎖,僅在Serializable
級別會加共享讀鎖select ... from ... lock in share mode
語句使用當前讀,加共享讀鎖(S鎖)select ... from ... for update
語句,爲當前讀,加排他寫鎖(X鎖)接下來咱們將按照不一樣的場景逐個不一樣語句的加鎖過程進行分析。以下爲使用到的表格:segmentfault
CREATE TABLE `t_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`no` char(18) NOT NULL DEFAULT '' COMMENT '身份證',
`name` varchar(50) NOT NULL DEFAULT '' COMMENT '姓名',
`age` int(4) NOT NULL DEFAULT '0' COMMENT '年齡',
PRIMARY KEY (`id`),
UNIQUE KEY `no` (`no`),
KEY `name` (`name`)
) ENGINE=InnoDB COMMENT='用戶表';
複製代碼
默認插入數據以下:bash
id | no | name | age |
---|---|---|---|
1 | 0001 | 張三 | 20 |
3 | 0003 | 李四 | 25 |
5 | 0005 | 王五 | 50 |
7 | 0007 | 王五 | 23 |
9 | 0009 | 趙六 | 28 |
Read Uncommitted
級別是事務隔離的最低級別,在此隔離級別下會存在髒讀的現象,會影響到數據的正確性,所以咱們在平常開發過程當中不多使用該隔離級別。在此隔離級別下更新語句採起的是普通的加行鎖的機制,Read Committed
的加鎖過程與Read Uncommitted
一致。因爲Read Committed
使用範圍較Read Uncommitted
更廣,在Read Committed
級別下詳細分析。數據結構
Read Committed
級別採起了一致性讀策略,解決了事務的髒讀問題,咱們如下簡稱爲RC級別
。在此級別下更新語句加鎖與Read Uncommitted
一致,可能存在的鎖有行鎖與意向鎖。加鎖過程採起了Semi-consistent read
優化策略,對於掃描過的數據如若不匹配,加鎖後會當即釋放。併發
假設咱們須要在上述t_user
表格中,刪除ID=7的王五這一條記錄,語句爲:性能
delete from t_user where id = 7;
複製代碼
因爲使用了主鍵,只需對該條記錄加X鎖便可,其加鎖過程以下: 優化
假設咱們經過身份證no
這個惟一索引來刪除id=7這條數據會如何加鎖呢?ui
delete from t_user where no = '0007';
複製代碼
因爲惟一索引爲二級索引,Innodb 首先經過惟一索引對數據進行過濾,對於0007惟一索引加X鎖,而後還須要在彙集索引上對主鍵=7的數據進行加X鎖。
假設咱們使用非惟一索引,那麼狀況又會如何呢?
delete from t_user where name = '王五';
複製代碼
因爲惟一索引爲二級索引,Innodb 首先經過索引對數據進行過濾,對於王五的兩條索引加X鎖,而後還須要在彙集索引上對主鍵=5,7 的數據進行加X鎖。
若是不使用任何索引,狀況會是怎樣呢?
delete from t_user where age = 23;
複製代碼
因爲刪除語句沒有使用任何索引,那麼 InnoDB 必須進行全表掃描以肯定哪條數據須要刪除。也就是說首先須要對全表的全部數據進行加鎖,InnoDB 在RC級別下
的加鎖過程採起了Semi-consistent read
優化策略,對於掃描過的數據如若不匹配,加鎖後會當即釋放。
那麼對於插入過程,RC級別
又是如何加鎖的呢?
insert into t_user(id,no,name,age) values(4,'00004','小灰灰',8);
複製代碼
InnoDB事實上只對主鍵加了X鎖。
Read Repeatable
級別引入了間隙鎖等一系列機制,來防止其餘事務的插入操做,如下簡稱RR級別
。但與此同時間隙鎖的範圍也帶來了不少額外的開銷與問題,其中之一就有因爲引入了間隙鎖加大了鎖的粒度範圍,使用不當容易形成死鎖。因爲RR級別
下能夠經過參數innodb_locks_unsafe_for_binlog
來配置是否開啓gap鎖,在此咱們討論的是開啓gap鎖的狀況。
假設咱們須要在上述t_user
表格中,刪除ID=7的王五這一條記錄,語句爲:
delete from t_user where id = 7;
複製代碼
因爲使用了主鍵,能夠惟一確認影響的記錄,只需對該條記錄加X鎖便可,其加鎖過程與RC級別
下的使用主鍵加鎖過程相同。
假設咱們經過身份證no
這個惟一索引來刪除id=7這條數據會如何加鎖呢?
delete from t_user where no = '0007';
複製代碼
因爲惟一索引爲二級索引,Innodb 首先經過惟一索引對數據進行過濾,對於0007惟一索引加X鎖,而後還須要在彙集索引上對主鍵=7的數據進行加X鎖。
假設咱們使用非惟一索引,那麼狀況又會如何呢?
delete from t_user where name = '王五';
複製代碼
因爲使用索引爲二級索引,Innodb 首先經過索引對數據進行過濾,因爲普通索引不能保證影響數據範圍惟一,有可能其餘的事務在對兩者之間的間隙操做添加新數據,所以還須要對於王五之間的間隙進行加鎖,以防有其餘事務在事務提交前在此間隙插入數據,最後還須要在彙集索引上對主鍵=5,7 的數據進行加X鎖。
那麼在RR級別
下,若是不使用索引會致使什麼狀況呢?
delete from t_user where age = 23;
複製代碼
如若不使用任何索引,InnoDB只可以經過全表掃描以肯定須要刪除的數據,所以首先會須要對全部數據進行加鎖,此外因爲須要避免其餘事務插入,還須要對全部的間隙進行加鎖,這對InnoDB性能影響很是顯著。
在RR級別下
,插入過程是如何加鎖的呢?
insert into t_user(id,no,name,age) values(4,'00004','小灰灰',8);
複製代碼
插入過程是不須要增長gap鎖的,所以RR級別
下的加鎖過程與RC級別
下的加鎖過程差很少。依照官方文檔,插入過程隱式的加了插入意向鎖,該鎖雖然爲間隙鎖,但大多數時候並不會影響其餘行的插入。
Serializable
級別是事務隔離的最高級別,在此級別下全部的請求會進行串行化處理。在InnoDB
中該級別下的 更新語句加鎖過程與Read Repeatable
下一致。