鎖的概念
- 鎖是什么
鎖是數據庫中在並發操作情形下保護資源的機制。通常(具體要看鎖兼容性)只有鎖的擁有者才能對被鎖的資源進行操作,從而保證數據一致性。
- 鎖的概念可分為幾部分
- 鎖資源(鎖住什么)
- 鎖模式(怎么鎖法)
- 鎖持續時間
- 兼容性
- 鎖的行為(鎖轉換,鎖升級)
1.鎖的資源
2.鎖的模式
- 共享鎖:Shared Lock,S Lock. 通常情況下,讀取數據時會對數據加上S Lock。
- 排它鎖: Exclusive Lock,X Lock。對數據進行更改(insert update,delete)時加X Lock
- 更新鎖:Update Lock,U Lock(或叫UPD Lock)。通常對數據進行Update操作會加U鎖。查找數據時會加U鎖,找到后對數據進行更改時在轉換為X鎖。U鎖是為了防止在並發對數據進行更新時出現死鎖,因為如果先加S鎖再轉換為X鎖,由於S鎖和S鎖兼容,但X鎖和S鎖不兼容,所以有可能出現死鎖。
- 意向鎖:Intent Lock(例如:IX,IU,IS),是指對被鎖定的資源的上層資源加鎖。意向鎖是為了提高鎖的效率。例如對行加X鎖,會對表加IX鎖(意向排他鎖),如果其他線程或事物想對該表加X鎖,就不用逐行檢查是否有其他所,只需檢查是否有IX鎖(或其他意向鎖)
3.鎖在事務中的持續時間
不同的事務隔離級別下,鎖有不同的持續時間。(單一個SQL語句也是一個事物,稱為“自動提交事務”,用begin tran/commit聲明的是顯式事務)
- Read uncommitted: select不會加鎖(no lock),但更新會加U鎖並持續到事務介紹
- Read committed:select加S鎖,讀完釋放。U鎖和X鎖持續到事務結束
- Repeatable Read : select加S鎖,但讀完不釋放,和U鎖,X鎖一樣持續到事務結束。
- Serializable :select會加范圍鎖,讀完不釋放,和U鎖,X鎖一樣持續到事務結束。
4.鎖兼容性
鎖兼容性是指不同模式的鎖能不能共存。鎖兼容性對照圖:
圖片出自Microsoft
5.鎖轉換和鎖升級
- 鎖轉換:鎖會由一種模式轉換為另一種模式。例如delete一條數據,先在數據查找時對數據加上U鎖,再在准備刪除數據前轉換成X鎖。
- 鎖升級:為了提高效率,鎖會從一種粒度升級到另一種粒度。一個鎖大概需要96B空間,如果鎖太多會占用太多資源。如果一個操作需要對很多資源加鎖,SQL會自動對這個資源的上級加相同的鎖,即鎖升級
死鎖的概念
死鎖就是兩個或多個會話(SPID)相互請求對方持有的鎖資源,導致循環等待的情況。
死鎖的檢查和分析
在Profiler中使用Deadlock Graph分析死鎖
捕獲到死鎖圖
也可以使用ErrorLog來記錄死鎖
- 打開trace flag
- dbcc traceon(1204,-1)
- dbcc traceon(1222,-1)
- 清空errorLog(不一定需要清空,只為減少log數據)
- dbcc errorlog或者 exec sp_cycle_errorlog
- 打開errorlog文件或者執行 xp_readerrorlog
- 分析死鎖圖,如下圖:
根據errorlog內容畫出死鎖圖,其中:
- NODE:代表每個死鎖節點
- Key/RID/Object:被鎖定的資源
- Grant List:擁有該資源的鎖的SPID
- Reuested List:請求該資源的SPID
- SPID:會話ID
- MODE: 該資源被鎖定的模式。參考:鎖模式
- Input Buf:發生死鎖時的SQL語句
這樣就可以畫出死鎖圖:
這樣便可以分析出死鎖的情況,當然如果對errorlog已經比較熟悉,就可以不用畫出死鎖圖了。
死鎖和索引
- 如果被鎖的資源是一個Object,只有object_id,可以通過Object_name()函數獲取object的名字。
- 如果是RID,可以通過 dbcc page指令去知道哪行數據被鎖。
- 如果是key,需要通過 %%lockres%%列,或者“dbcc ind指”令和“dbcc page”指令去知道哪個索引鍵被鎖。
- 在死鎖節點的Key中,顯示被鎖的key為:
KEY: 49:72057594189512704 (11036c0fa4d3),格式為:
KEY: db_id:hobt_id(index key hash value)
SQL SERVER通過對一個由索引鍵值生成的hash value進行鎖定,來達到對索引鎖定的目的。其中(11036c0fa4d3)就是某個key的hash value。
如何由hash value得知是哪行數據被鎖定?請按以下步驟:
如果是聚集索引,可以使用%%lockres%%隱藏列獲取lock hash value:
如果是非聚集索引,%%lockres%%只顯示 RID。如果需要確定數據行對應的索引頁中的索引鍵,需要使用dbcc ind和dbcc page指令進行分析:
- 要查看索引的lock hash value,需要查看索引所在的數據頁的內容。
- 要知道索引在什么數據頁中,需要使用dbcc ind指令:dbcc ind(‘moe_dev’,592773219,3) “592773219”代表表的object_id,3代表索引的index_id 。可使用dbcc ind指令:
知道了索引所在數據頁的Page_ID,可以使用dbcc page指令查看數據頁內容(最后一個參數必須為“3”):
知道了索引所在數據頁的Page_ID,可以使用dbcc page指令查看數據頁內容(最后一個參數必須為“3”):
知道了key hash value和Heap RID,可以通過下面的SQL語句轉換成RID
DECLARE @HeapRid BINARY(8) SET @HeapRid = 0xDB27000001001700 SELECT CONVERT (VARCHAR(5), CONVERT(INT, SUBSTRING(@HeapRid, 6, 1) + SUBSTRING(@HeapRid, 5, 1))) + ':' + CONVERT(VARCHAR(10), CONVERT(INT, SUBSTRING(@HeapRid, 4, 1) + SUBSTRING(@HeapRid, 3, 1) + SUBSTRING(@HeapRid, 2, 1) + SUBSTRING(@HeapRid, 1, 1))) + ':' + CONVERT(VARCHAR(5), CONVERT(INT, SUBSTRING(@HeapRid, 8, 1) + SUBSTRING(@HeapRid, 7, 1))) AS 'Fileid:Pageid:slot'
得到的RID為:
通過dbcc page指令就可以查看是哪行被鎖定了。
參考資料
有關死鎖的資料和比較好的文章,請看另一篇博文《有關DeadLock的文章列表》