一、鎖簡介
鎖這個詞是比較常見的,生活中我們使用鎖來保證一個房間或者一個資源的安全,因爲開鎖需要鑰匙,而鑰匙保存在我們手裏,其他人是無法正常獲取到的。程序中,當我們的程序需要多線程去訪問操作共享資源時,爲了保證一致性,我們需要使用鎖機制來防止併發原因出現的問題,同樣,數據庫會使用這種鎖機制來保證資源的共享安全性,比如當兩個事務都需要更改同一條記錄時,就需要鎖機制來保證一致安全性。
下面根據加鎖的範圍來看下innodb支持的鎖類別:
1.記錄鎖(行鎖)
行級鎖是特定引擎所支持的,比如Innodb是支持行鎖的,這也是它的一個特性,可以在數據記錄級別上鎖,粒度是比較細的,記錄鎖在加鎖類別可以分成兩種鎖:
共享鎖(s鎖):事務可以讀取數據,如果數據加了記錄共享鎖,可以繼續加共享鎖;
排他鎖(x鎖):事務可以更改數據,如果事務加了記錄排他鎖,則不可以在家共享鎖和排他鎖;
兩種鎖的排斥關係如下:
鎖 | 共享鎖(s) | 排他鎖(x) |
共享鎖(s) |
兼容 | 不兼容 |
排他鎖(x) |
不兼容 | 不兼容 |
2.範圍鎖
範圍鎖是指加在區間上的鎖,其鎖定的是一個空間,比如鎖定範圍爲(-∞,1】或(10,30)這種方式,Innodb中這種鎖叫做gap鎖。
間隙鎖(gap lock):工作在事務隔離級別中的RR級別,在索引之間加入鎖,它使得RR級別避免了幻讀;
next key lock:這種鎖描述的在Innodb中會出現行鎖和gap lock結合使用的場景,把這種加鎖方式叫做next key;
3.粗粒度鎖
Innodb還支持粗粒度的鎖,可以建立在表以及段、區、頁上的。其中意向鎖是表級別的鎖,當想要在數據記錄上加x鎖時,需要先在表加上IX鎖。意向鎖之間是兼容的,當我們給表加表鎖的時候,如果沒有意向鎖的話,需要遍歷每一條數據是否有排他鎖,如果沒有才能加表鎖,但是有了意向鎖,直接判斷表上是否有意向排他鎖就可以了。意向鎖是Innodb引擎自我管理的,無需DBA去操作或者管理配置,是一種自我屬性。類別上也是分爲兩種:
意向共享鎖(IS):在數據記錄加排他鎖之前,需要在上級也就是表上加意向排他鎖;
意向排他鎖(IX):在數據記錄加共享鎖之前,需要在上級也就是表上加意向共享鎖;
意向鎖和記錄鎖之間的排斥關係如下:
鎖類別 | 共享鎖 | 排他鎖 | 意向共享鎖 | 意向排他鎖 |
共享鎖 | 兼容 | 不兼容 | 兼容 | 不兼容 |
排他鎖 | 不兼容 | 不兼容 | 不兼容 | 不兼容 |
意向共享鎖 | 兼容 | 不兼容 | 兼容 | 兼容 |
意向排他鎖 | 不兼容 | 不兼容 | 兼容 | 兼容 |
注:IS或者IX鎖此時是與表級的X鎖或S鎖比較排斥關係
二、加鎖情況
在Innodb引擎中,使用多版本併發控制來處理併發的問題,也就是MVCC(Multi-version Concurrency Control),這個控制方法不僅僅使用在Innodb中,其實已經很多數據庫,以及其他很多的引擎也支持使用了這中控制協議,其最大的有點是讀不加鎖,讀寫不衝突,因此減少了鎖的開銷,提高了讀寫性能,可以理解他是行級鎖的一個變種。
MVCC工作在Innodb的RC和RR事務隔離級別下,因爲RU每次都讀取最新的行,Seriallizable是單線程串行,無需加鎖操作。
在Innodb的MVCC中讀操作分爲兩種:快照讀和當前讀,快照讀讀取的數據可見版本或者歷史版本,無需加鎖操作,當前讀需要讀取數據的最新數據行,並且這些返回的數據行都會加鎖,防止其他事物更改,兩種讀操作包含的sql語句結構:
快照讀:select * from table where ***;簡單的讀取操作;
當前讀:
select * from table where ? lock in share mode;//加S鎖,其他事物可以讀取;
select * from table where ? for update;
insert into table values(...);
update table set ...where ?;
delete from table where ?;
除了第一條加的是S鎖,其他語句都是讀取數據的最新行,並且需要加X鎖,以防止其他事物更改或者讀取。
通過一條update語句,分析sql執行的簡單過程:
1.首先客戶端收到一條sql,客戶端將這個sql發到server層,server經過權限驗證、查詢緩存(開啓)、sql優化、指定執行計劃等步驟之後,會把該sql發送到引擎層;
2.引擎層接收到該sql後,會查找符合該條件的記錄,先去內存查找,如果沒有,則到磁盤讀取放入內存,找到符合條件的記錄返回到server層執行器,並給該記錄執行加鎖操作;
3.執行器根據該返回記錄,生成新的行,調用引擎的寫入接口,將該記錄寫入;
4.引擎層接收到該更新插入請求,更新內存,添加日誌,將更新成功結果返回到server層,一條記錄更新完成;
5.引擎繼續返回滿足條件的行,直到所有滿足條件更新完畢,就會告訴執行器所有記錄更新完成,可以提交;
6.server層發起提交請求,引擎層執行提交操作,完成;
可以看出更新的過程是有一個當前讀加鎖操作,插入記錄如果需要檢查unique key衝突,也需要當前讀的驗證;
三、鎖情況分析
鎖的定義瞭解之後,我們需要知道我們應用中sql執行的內部流程包括加鎖條件 ,這樣纔能有方向的進行優化,提升速度。假如我們分析一條語句的加鎖條件,例如:delete from T1 where id = 10;執行的過程受很多因素參數影響,比如當前引擎的事務隔離級別、id是否有索引,如果有索引,索引的類別是什麼?是主鍵?是唯一索引?下面列出幾種情況,講述鎖的使用方式,可以通過閱讀文章《Mysql—Innodb引擎 索引》瞭解innodb索引信息:
假設有表T1,有字段id,name:
RC級別下:
1.RC級別、id主鍵
id是主鍵,直接在主鍵上加x鎖就可以
2.RC級別、id輔助唯一索引
id爲唯一輔助索引,則需要在輔助索引x鎖,在通過主鍵x鎖,便可確定
3.RC級別、id輔助非唯一索引
將符合條件的非唯一輔助索引上加x鎖、通過輔助索引找到對應的主鍵並加x鎖
4.RC級別、id無索引
id列無索引,則通過全表掃描找到對應的數據記錄,這個過程會使得全部記錄加x鎖。因爲全部加x鎖會嚴重影響性能,後期優化後,在尋找過程中,不符合記錄的記錄會去掉x鎖
RR級別下:
1.RR級別、id主鍵
和RC級別下,id主鍵的情況相同,都是在主鍵上加x鎖;
2.RR級別、id輔助唯一索引
和RC級別下,id輔助唯一索引的情況相同,都是在對應輔助索引和主鍵上加x鎖;
3.RR級別、id輔助非唯一索引
此種情況下會加入gap鎖,加上記錄鎖,也就是在輔助索引上加next key lock,在對應主鍵x加鎖
4.RR級別、id無索引
全變掃描全部x鎖加gap鎖,優化後,不符合記錄的記錄去掉x鎖,也不會加gap鎖;
四、資源地址
文檔:《Mysql技術內幕-innodb存儲引擎》《高性能Mysql》