選擇/插入死鎖
此實例託管 SharePoint 2007 數據庫 (SP)。我們已經在 SP 內容數據庫中針對一個使用率很高的表遇到了許多
SELECT
/死鎖。INSERT
我已經縮小了所涉及的資源,兩個程序都需要鎖定非聚集索引。INSERT
需要IX
對SELECT
資源進行鎖定,並且需要SELECT
對資源進行S
鎖定INSERT
。死鎖圖描繪了三個資源,
1.)兩個來自
SELECT
(生產者/消費者並行執行緒),和2.) 的
INSERT
。我已附上死鎖圖供您查看。因為這是 Microsoft 程式碼和表結構,我們無法進行任何更改。但是,我在 MSFT SP 站點上讀到,他們建議將 MAXDOP 實例級別配置選項設置為 1。由於此實例在許多其他數據庫/應用程序之間共享,因此無法禁用此設置。
因此,我決定嘗試阻止這些
SELECT
陳述並行。我知道這不是解決方案,而是臨時修改以幫助進行故障排除。因此,我這樣做時將“並行成本門檻值”從我們的標準 25 增加到 40,即使工作負載沒有改變(SELECT
/INSERT
經常發生)死鎖已經消失。我的問題是為什麼?SPID 356 INSERT 在屬於非聚集索引的頁面上具有 IX 鎖
SPID 690 SELECT 執行 ID 0 在屬於同一非聚集索引的頁面上具有 S 鎖
現在:
SPID 356 想要 SPID 690 資源上的 IX 鎖,但無法獲得它,因為 SPID 356 被 SPID 690 執行 ID 0 S 鎖
SPID 690 執行 ID 1 想要 SPID 356 資源上的 S 鎖,但無法獲得它,因為 SPID 690 執行 ID 1 被 SPID 356 阻塞,現在我們遇到了死鎖。
如果有人可以幫助我理解為什麼我會非常感激。
事件接收器表:
Id uniqueidentifier no 16 Name nvarchar no 512 SiteId uniqueidentifier no 16 WebId uniqueidentifier no 16 HostId uniqueidentifier no 16 HostType int no 4 ItemId int no 4 DirName nvarchar no 512 LeafName nvarchar no 256 Type int no 4 SequenceNumber int no 4 Assembly nvarchar no 512 Class nvarchar no 512 Data nvarchar no 512 Filter nvarchar no 512 SourceId tContentTypeId no 512 SourceType int no 4 Credential int no 4 ContextType varbinary no 16 ContextEventType varbinary no 16 ContextId varbinary no 16 ContextObjectId varbinary no 16 ContextCollectionId varbinary no 16 index_name index_description index_keys EventReceivers_ByContextCollectionId nonclustered located on PRIMARY SiteId, ContextCollectionId EventReceivers_ByContextObjectId nonclustered located on PRIMARY SiteId, ContextObjectId EventReceivers_ById nonclustered, unique located on PRIMARY SiteId, Id EventReceivers_ByTarget clustered, unique located on PRIMARY SiteId, WebId, HostId, HostType, Type, ContextCollectionId, ContextObjectId, ContextId, ContextType, ContextEventType, SequenceNumber, Assembly, Class EventReceivers_IdUnique nonclustered, unique, unique key located on PRIMARY Id
從表面上看,這看起來像是一個經典的查找死鎖。這種死鎖模式的基本要素是:
SELECT
使用帶有鍵查找的非覆蓋非聚集索引的查詢INSERT
修改聚集索引和非聚集索引的查詢首先
SELECT
訪問非聚集索引,然後是聚集索引。首先INSERT
訪問聚集索引,然後是非聚集索引。以不同的順序訪問相同的資源獲取不兼容的鎖當然是“實現”死鎖的好方法。在這種情況下,
SELECT
查詢是:…
INSERT
查詢是:注意綠色突出顯示的非聚集索引維護。
我們需要查看
SELECT
計劃的串列版本,以防它與並行版本有很大不同,但正如 Jonathan Kehayias 在他的處理死鎖指南中指出的那樣,這種特殊的死鎖模式對時間和內部查詢執行細節非常敏感。這種類型的死鎖經常在沒有明顯外部原因的情況下出現和消失。鑑於對相關係統的訪問權和適當的權限,我確信我們最終可以準確地找出為什麼會發生死鎖的原因是並行計劃而不是串列計劃(假設相同的總體形狀)。潛在的查詢線包括檢查優化的嵌套循環和/或預取 - 這兩者都可以在內部將隔離級別升級到
REPEATABLE READ
語句的持續時間。並行索引查找範圍分配的某些功能也可能導致該問題。如果連續計劃可用,我可能會花一些時間進一步研究細節,因為它可能很有趣。這種類型的死鎖的通常解決方案是使索引覆蓋,儘管在這種情況下列的數量可能會使其不切實際(此外,我們不應該在 SharePoint 上搞砸這些事情,我被告知)。最終,在使用 SharePoint 時推薦僅串列計劃是有原因的(雖然不一定是好的,但歸根結底)。如果並行性成本門檻值的變化暫時解決了這個問題,那很好。從長遠來看,我可能會考慮分離工作負載,可能會使用資源調控器,以便 SharePoint 內部查詢獲得所需的
MAXDOP 1
行為,並且其他應用程序能夠使用並行性。僵局痕跡中出現的交換問題對我來說似乎是一個紅鯡魚;僅僅是獨立執行緒擁有技術上必須出現在樹中的資源的結果。我看不出有任何跡象表明交易所本身直接導致了僵局問題。
如果這是典型的查找死鎖,則資源列表將包括聚集索引和非聚集索引。通常,SELECT 將持有 NC 索引上的 SHARED 鎖並等待 CI 上的 SHARED 鎖,同時 INSERT 將獲取 CI 上的 EXCLUSIVE 鎖並等待 NC 上的 EXCLUSIVE 鎖。在這種情況下,死鎖 xml 中的資源列表將列出這兩個對象。
由於死鎖圖僅涉及 NC 索引,我們可以排除該選項。
此外,如果這是由於使用 UNORDERED PREFETCH 的 Nested Loop Join 而導致的死鎖,執行計劃將告訴我們是否使用了 UNORDERED PREFETCH 算法,這裡又不是這種情況(請參閱下面的更新)。
這讓我們假設這是由於並行計劃導致的僵局。
死鎖圖未正確呈現,但如果您查看死鎖 XML,您可以看到來自 SELECT 語句 (SPID 690) 的兩個執行緒參與了死鎖。消費者執行緒在 PAGE 1219645 上持有一個共享鎖,並在埠 801f8ed0 (e_waitPipeGetRow) 上等待生產者。生產者執行緒正在等待 PAGE 1155940 上的共享鎖。
INSERT 語句持有 PAGE 1155940 上的 IX 鎖並等待 PAGE 1219645 上的 IX 鎖,從而導致死鎖。
我相信在對 SELECT 語句使用串列計劃時可以避免死鎖,因為它在任何時候都不需要超過一頁的共享鎖。我還認為串列計劃與並行計劃幾乎相同(沒有並行運算符)。
$$ UPDATED based on Paul’s comment $$ 顯然該計劃使用的是優化的嵌套循環算法
這就解釋了為什麼 SHARED 鎖會一直保留到語句結束。REPEATABLE READ 與並行計劃相結合比串列計劃更容易發生死鎖,因為並行計劃可能會從索引的不同範圍獲取並保留鎖,而串列計劃以更順序的方式獲取鎖。