2013-01-15 56 views
0

我有一個複雜的工作單元,它可能會將更改提交到10-15個表作爲單個事務處理。工作單元在快照隔離下執行。異步執行SQL或從觸發器更改鎖定

某些表具有執行存儲過程以將消息記錄到隊列中的觸發器。該消息包含表名,密鑰和更改類型。這是爲了向SQL2005提供向後兼容性所必需的,我不能使用內置隊列。

問題是我正在寫入存儲過程的隊列中發生阻塞和超時。我要麼收到消息說:

Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation to access table 'dbo.tblObjectChanges' directly or indirectly in database 

或者我得到超時寫入該表。

有沒有辦法改變從觸發器內執行消息隊列寫入的存儲過程到(或在其內部)的特定調用的事務隔離?作爲最後的手段,我可​​以調用刪除或更新部分存儲過程異步運行嗎?

下面是SQL存儲過程:

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
ALTER PROCEDURE [dbo].[usp_NotifyObjectChanges] 
    @ObjectType varchar(20), 
    @ObjectKey int, 
    @Level int, 
    @InstanceGUID varchar(50), 
    @ChangeType int = 2 

AS 

SET NOCOUNT ON 

DECLARE @ObjectChangeID int 

--Clean up any messages older than 10 minutes 
DELETE from tblObjectChanges Where CreatedTime < DATEADD(MINUTE, -10, GetDate()) 

--If the object is already in the queue, change the time and instanceID 
SELECT @ObjectChangeID = [ObjectChangeID] FROM tblObjectChanges WHERE [ObjectType] = @ObjectType AND [ObjectKey] = @ObjectKey AND [Level] = @Level 

IF NOT @ObjectChangeID is NULL 
BEGIN 
    UPDATE [dbo].[tblObjectChanges] SET 
     [CreatedTime] = GETDATE(), InstanceGUID = @InstanceGUID 
    WHERE 
     [ObjectChangeID] = @ObjectChangeID 
END 
ELSE 
BEGIN 
    INSERT INTO [dbo].[tblObjectChanges] (
     [CreatedTime], 
     [ObjectType], 
     [ObjectKey], 
     [Level], 
     ChangeType, 
     InstanceGUID 
    ) VALUES (
     GETDATE(), 
     @ObjectType, 
     @ObjectKey, 
     @Level, 
     @ChangeType, 
     @InstanceGUID 
    ) 
END 

tblObjectChanges的定義:

CREATE TABLE [dbo].[tblObjectChanges](
    [CreatedTime] [datetime] NOT NULL, 
    [ObjectType] [varchar](20) NOT NULL, 
    [ObjectKey] [int] NOT NULL, 
    [Rowversion] [timestamp] NOT NULL, 
    [Level] [int] NOT NULL, 
    [ObjectChangeID] [int] IDENTITY(1,1) NOT NULL, 
    [InstanceGUID] [varchar](50) NULL, 
    [ChangeType] [int] NOT NULL, 
CONSTRAINT [PK_tblObjectChanges] PRIMARY KEY CLUSTERED 
(
    [ObjectChangeID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY] 
) ON [PRIMARY] 

GO 
+0

你不能在SQL Server 2005中使用Service Broker嗎?爲什麼不? –

+0

你得到超時會讓我懷疑。是否有其他長時間運行的交易阻止重要數據? – usr

+0

有問題的超時總是在調用該存儲過程時。 – Molloch

回答

2

此行幾乎肯定是你的問題:

DELETE from tblObjectChanges Where CreatedTime < DATEADD(MINUTE, -10, GetDate()) 

有兩種BIG問題這一發言。首先,根據你的表定義,創建時間不是編入索引的。這意味着爲了執行這個語句,必須掃描整個表,並且這將導致整個表在這種情況成爲的一部分的任何事務期間被鎖定。所以在這個專欄上放一個索引。

第二個問題是,即使使用索引,您實際上也不應該在觸發器內執行像這樣的操作維護任務。除了減慢必須執行它的OLTP事務外,這個語句只需要每5-10分鐘執行一次。相反,您隨時可以執行任何操作(並且每時間),這些表中的任何一個都會被修改。隨着系統變得越來越忙,這會增加很多額外負載。

更好的方法是將此語句完全從觸發器中取出,而每隔5-10分鐘運行一次SQL代理作業以執行此清理操作。如果您在添加索引的同時執行此操作,則大部分問題都會消失。


另外一個問題是這樣的陳述:

SELECT @ObjectChangeID = [ObjectChangeID] FROM tblObjectChanges WHERE [ObjectType] = @ObjectType AND [ObjectKey] = @ObjectKey AND [Level] = @Level 

不同於上述的第一條語句,該語句在觸發所屬。然而,就像第一條語句一樣,它也會在負載下產生(並導致)嚴重的性能和鎖定問題,因爲再次根據您發佈的表定義,沒有搜索到的列被索引。

解決方案再次是在這些列上添加一個附加索引。

+0

謝謝。我知道刪除操作效率低下,但大多數實例都是針對SQL Express運行的,並且此表中一次只有30-50條記錄,只有大約10個表使用此「消息隊列」。我會做出這些改變並找出一個更好的方法來避免經常運行刪除操作,並看看它是如何發生的。謝謝你的幫助。 – Molloch

+0

我結束了更改代碼,以返回表上的時間戳的視圖中的最後更新的值。再次感謝。 – Molloch

1

幾個想法:

  • 移動刪除到一個單獨的預定工作,如果可能
  • 在CreatedTime上添加索引
  • 添加索引的對象類型,ObjectKey,級別
  • WITH(UPDLOCK,ROWLOCK)添加到SELECT
  • 添加WITH(ROWLOCK)的INSERT和UPDATE

您需要測試所有這些看看有什麼幫助。我會按照這個順序通過它們,但是請看下面的註釋。

即使您決定反對這一切,至少將WITH(UPDLOCK)留在SELECT上,否則可能會丟失更新。