Tablock 提示觸發死鎖
我使用最少的日誌記錄將兩個數據集插入到一個空堆表中,使用兩個並行執行的執行 SQL 任務並使用以下形式的 SQL。
INSERT INTO Table (TABLOCK) SELECT FROM ...
作業掛起後,其中一個 SQL 任務成為死鎖犧牲品。下面是死鎖圖的 XML 輸出。
有人可以解釋引擎蓋下發生了什麼嗎?
<resource-list> <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746"> <owner-list> <owner id="process9609dc8" mode="Sch-S"/> <owner id="process9609dc8" mode="IX"/> </owner-list> <waiter-list> <waiter id="process5e13048" mode="X" requestType="convert"/> </waiter-list> </objectlock> <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746"> <owner-list> <owner id="process5e13048" mode="Sch-S"/> <owner id="process5e13048" mode="IX"/> </owner-list> <waiter-list> <waiter id="process9609dc8" mode="X" requestType="convert"/> </waiter-list> </objectlock> </resource-list>
事情變得更加棘手,因為我發現在大多數情況下,兩個執行 SQL 任務可以成功並行執行。試試下面:
Create table dbo.TablockInsert (c1 int, c2 int, c3 int) --then issue the script in two Execute Sql Task in parallel you won't fail: insert into dbo.TablockInsert(TABLOCK) SELECT 1, 1, 1
由於唯一的區別是 SELECT… FROM… 語句,看起來 SELECT… FROM… 語句會影響這裡的鎖定模式?
數據載入性能指南是為 SQL Server 2008 編寫的,但據我所知,微軟在這方面沒有對堆進行任何改進。這是您的載入方案的報價:
批量載入一個未分區的空表
將數據載入到非分區表中雖然是一個簡單的操作,但可以通過多種方式進行優化。
…
僅當所選批量方法對錶發出批量更新 (BU) 鎖時,才可能對堆進行多個並發插入操作。兩個批量更新 (BU) 鎖是兼容的,因此兩個批量操作可以同時執行。
在這種情況下,INSERT … SELECT和 SELECT INTO 都有一個缺點。這兩個操作都在目標上採用排他 (X) 表級鎖定。這意味著在給定時間只能執行一個批量載入操作,從而限制了可伸縮性。但是,如果您指定 TABLOCK 提示,BCP、BULK INSERT 和集成服務都能夠獲取批量更新 (BU) 鎖。
重要的部分是您沒有獲得 BU 鎖
INSERT ... SELECT
。您將始終在表上獲得排他鎖,因此一次INSERT
只能執行一個。在評論中,您說您將插入 100k 行或更少,並且在插入期間其他程序不會在表上執行。當向數據庫發送兩個 INSERT 查詢時,我預計會發生以下三件事之一:
- 一個插入首先執行並阻塞另一個插入。第二次插入等到第一次插入完成。
- 一個插入在第二個插入開始之前完成。沒有顯式阻塞,但它們不會同時執行。
- 您會遇到死鎖,並且只有一個插入成功完成。
在所有情況下,向查詢中添加提示都會使您受益或不會受到傷害
TABLOCKX
,因此這是我解決死鎖的建議。如果您想知道為什麼有時會發生死鎖,您需要尋找另一個答案。對於真正需要並行插入的不同場景,解決 BU 問題的兩種方法是對堆進行分區並將每個會話插入到單獨的分區中,或者通過 BCP、BULK INSERT 或集成服務載入數據.
dbo.TargetTable
您正在從兩個會話中插入,並且都使用TABLOCK
提示 。兩者都process9609dc8
和process5e13048
程序保持Sch-S
以及IX
彼此兼容的鎖,因此兩個程序可以同時保持。但兩者都想將IX
鎖轉換為Exclusive X
類型。X
鎖彼此不兼容。因此,SQL Server 選擇了其中一個會話作為死鎖犧牲品,而不是無限等待對方。