1. 程式人生 > 實用技巧 >SQL Server 死鎖

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 Selectdb_name(5)

開啟這個資料庫,執行如下SQL語句,可以查詢這個Page 屬於誰.

1 2 3 4 5 selectobject_name(i.object_id), i.name fromsys.partitionsasp innerjoinsys.indexesasioni.object_id = p.object_idandi.index_id = p.index_id wherep.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個步驟: