SQL Server 死鎖
在資料庫中開啟死鎖監測可以收集到資料庫發生的死鎖情況。開啟的方式有2種:
1 開啟1222監控
執行SQL語句:
1 |
Dbcc traceon(1222,-1);
|
然後在系統日誌裡檢視死鎖的資訊。
2 啟動SQL Profiler(建議使用):
下面就是一個發生死鎖的例項圖:
下面提供對這個死鎖分析思路,如有不當之處,還望大家批評指正。
一共3個問題,下面逐個回答。
第一個問題:被鎖定的資源是什麼?
上面寫的很清楚,是一個Page 鎖, 那麼Page 鎖是什麼呢?
通常死鎖是你操作A表,然後又要操作B表,而另外一個程序先操作的B表,然後等待你釋放A表的鎖而導致的死鎖,這時看的圖上面就會明確說Table Lock,而不是Page Lock。
要解釋Page Lock 得先解釋下什麼是Page。
在SQL Server 中,資料存放的時候是放在一個8K的資料單元裡,這個資料單元稱為 Page.
讀取資料時,不是一筆一筆的讀取,而是一個Page 一個Page 的讀取,所以SQL Server 的文件中都會說,可能讀取了一些不需要的資料。
知道了Page 之後,怎麼會有Page 鎖呢? Table Lock ,Row Lock 比較容易理解,但是Page Lock 就不太容易理解,實際上,這時SQL Server 對於大表採取的一種它認為價效比最好的策略。如果採取表鎖,那麼一旦Update 資料,則別人就不能Select 了,否則會出現資料不一致的情況(就是所謂的髒讀,幻讀),但是你有可能會說,你修改你的,我讀取的並不是你要改的那一筆,你不要鎖定我,應該採取Row Lock,但是想象一下,對於一個有100萬筆資料的表來說,採取行鎖,成本得有多高?所以SQL Server採取了一箇中庸的方式,使用8K的資料頁Page作為鎖的單位,這樣就平衡了併發與效能的問題,價效比最高。
定位Page
一個表裡有很多Page,那麼本例中鎖住的Page 到底是什麼呢?
執行SQL語句查詢DB的名字
1 |
Select db_name(5)
|
開啟這個資料庫,執行如下SQL語句,可以查詢這個Page 屬於誰.
1 2 3 4 5 |
select object_name(i.object_id), i. name
from sys.partitions as p
inner join sys.indexes as i on i.object_id = p.object_id and i.index_id = p.index_id
where p.partition_id = '72057594048544768'
|
是一個非聚集的索引。
Page裡放了什麼?
目前我們已經瞭解了,它鎖定的是一個索引的一個數據頁(如果您對索引的Page 組織結構感興趣,請參考索引內部結構例項)。那麼鎖住的2個Page裡面存放了什麼資訊?
執行如下SQL:
1 2 3 |
DBCC TRACEON(3604)
DBCC Page (DBName,1,46574417,1)
|
結果如下:
執行如下SQL:
1 |
DBCC Page (DBName,1,93865146,1)
|
結果如下:
第二個問題:是誰在鎖住這些頁?
檢視之後發現:
1個SQL語句是Select
2個SQL 語句是Update
Select 語句為什麼要鎖定這2頁?
本例中的Select 語句使用了本索引,使用它來快速定位,那麼它根據索引的分支節點尋找,然後到指定的Page 中去讀取尋找,因為是Top 500,所以,可能找到一批資料,但是還不夠500,所以接著往其他頁裡找,直到找滿500筆為止
Update 語句為什麼要鎖定這2頁?
找到這筆資料直接修改不就完了嗎?並不是的,修改一個索引的值,至少要經過如下3個步驟: