2009-09-16 61 views
5

我打開快照隔離在我的數據庫使用下面的代碼快照上依然死鎖,ROWLOCK

ALTER DATABASE MyDatabase 
SET ALLOW_SNAPSHOT_ISOLATION ON 

ALTER DATABASE MyDatabase 
SET READ_COMMITTED_SNAPSHOT ON 

和擺脫掉大量的死鎖。

但我的數據庫仍然產生死鎖,當我需要每小時運行一個腳本來清理100,000多行時。

  • 有沒有一種方法可以避免我的清理腳本中發生死鎖,我是否需要在該查詢中專門設置ROWLOCK?
  • 有沒有辦法增加數據庫使用的行級鎖的數量?
  • 如何提升鎖定?從行級到頁級到表級?

我刪除腳本是相當簡單:

delete statvalue 
from statValue, 
(select dateadd(minute,-60, getdate()) as cutoff_date) cd 
where temporaryStat = 1 
and entrydate < cutoff_date 

現在我正在尋找快速解決方案,而是一個長期的解決方案會更好。

非常感謝, Patrikc

+0

你能發佈死鎖跟蹤信息嗎?有關如何執行此操作的示例,請參閱http://www.dalun.com/blogs/10.13.2006.htm。 – Justin 2009-09-16 15:53:13

+0

statValue在entryDate上有索引嗎?你看過查詢計劃嗎?我想知道你是否正在進行表掃描。 – automatic 2009-09-16 15:56:01

+0

猜測這裏沒有足夠的空間在這裏發佈死鎖蹤跡... entrytDate沒有索引,我使用StatValueID作爲索引。 在執行計劃中有索引查找以及聚簇索引查找。 – Patto 2009-09-16 16:08:44

回答

0

這樣做是爲了減少(或避免)的方式死鎖是批次,每個批次之間短暫的等待刪除(使用WAITFOR DELAY)

另外,死鎖可以通過具有覆蓋指數來減輕。

此代碼需要一些聲明,並且僅作爲示例(運行風險自負!)。

SELECT @intRowCount = 1, 
    @intErrNo = 0 

DECLARE @cutoff_date DATETIME 

SET @cutoff_date = dateadd(minute,-60, getdate()) 

SELECT @intRowsToDelete = COUNT(*) -- Number of rows to be deleted 
FROM dbo.statValue 
WHERE temporaryStat = 1 
AND entrydate < @cutoff_date 

WHILE @intRowCount > 0 AND @intErrNo = 0 
BEGIN 

    SET ROWCOUNT @DEL_ROWCOUNT 

    delete statvalue 
    FROM dbo.statValue 
    WHERE temporaryStat = 1 
    AND entrydate < @cutoff_date 

    SELECT @intErrNo = @@ERROR, @intRowCount = @@ROWCOUNT 

    SET ROWCOUNT 0 -- Reset batch size to "all" 

    SELECT @intRowsToDelete = @intRowsToDelete - @intRowCount 

    WAITFOR DELAY '000:00: 
END 

我還包括約翰的建議,不重複計算日期範圍標準。

+0

不要依賴set rowcount。它有問題,正在走向低谷。只需在循環中刪除top並在@@ rowcount = 0時解除:while(1 = 1)begin delete top(@del_count)where ...;如果@@ rowcount = 0 break; end'。 – 2009-09-16 16:40:34

+0

來自BOL:「使用SET ROWCOUNT不會影響下一版SQL Server中的DELETE,INSERT和UPDATE語句」 – 2009-09-16 16:41:49

+0

@Remus Rusanu:乾杯。我完全忘記了這一點。雖然它似乎懷疑它會被刪除... – 2009-09-17 07:55:01

0

請重新寫你刪除查詢,像這樣:

DECLARE @cutoff_date DATETIME 

SET @cutoff_date = dateadd(minute,-60, getdate()) 

delete statvalue 
from statValue 
where temporaryStat = 1 
and entrydate < @cutoff_date 

你會看到涉及讀取死鎖在你執行計劃的成本降低

6

快照隔離,只能緩解(部分),但它完全沒有辦法避免寫入和寫入死鎖。如果每小時生成100k +行,每秒插入約30個插入行,則刪除掃描幾乎可以保證與其他寫入操作發生衝突。如果你所做的只是插入,絕不更新,然後是刪除塊,但是在行級別鎖定時不會死鎖,但是因爲表格足夠大並且刪除正在執行掃描,引擎可能會爲刪除選擇一個頁面鎖定,因此可能是你得到的僵局。

如果在entrydate上沒有索引,delete只能掃描整個表。這種經常插入頂部並在底部刪除的表格實際上是隊列,您應該按照entrydate來組織它們。這意味着條目日期應該可能是聚簇索引中最左邊的。該組織允許清楚地分離在表的末尾發生的插入與在另一端發生的刪除。但這是一個相當激進的變化,特別是如果您使用statvalueid來讀取這些值。我猜你現在有一個基於自動增量字段(StatValueId)的聚集索引。另外我還假設entrydate和statvalueid是相關的。如果這兩個假設是真的,那麼你應該刪除基地唐的statvalueid:發現安全地刪除最大的ID,然後離開了這個ID的聚集索引中刪除的所有內容:

declare @statvalueidmax int; 
select @statvalueidmax = max(statvalueid) 
from statvalue with (readpast) 
where entrydate < dateadd(minute,-60, getdate()); 

delete statvalue 
where statvalueid <= @statvalueidmax; 

有許多假設我做了,他們可能是錯的。但這個想法的要點是,你必須將插入與刪除分開,以便它們不重疊。

+0

我喜歡這個想法,並會嘗試這個,但我不確定這是否會幫助我解決創建的死鎖問題。在未來,我希望更多的數據進入這個表格。 – Patto 2009-09-16 16:47:58

+1

有很多因素在起作用,並且我有很少的信息。但是你應該嘗試將表格想象成一條線(按照從左至右的順序排列):每小時批量作業在線左側消失,在當前活動熱點處插入lookus和更新,在右側邊緣。如果兩者不符合,則不會發生死鎖。 – 2009-09-16 16:56:26