2010-03-22 87 views
4

我有一個SQLServer 2008數據庫,其中有一個標籤表。標籤只是一個id和一個名字。標籤表的定義如下所示:SQL併發測試更新問題

CREATE TABLE [dbo].[Tag](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [Name] [varchar](255) NOT NULL 
CONSTRAINT [PK_Tag] PRIMARY KEY CLUSTERED 
(
    [ID] ASC 
)WITH (PAD_INDEX = OFF, 
     STATISTICS_NORECOMPUTE = OFF, 
     IGNORE_DUP_KEY = OFF,  
     ALLOW_ROW_LOCKS = ON, 
     ALLOW_PAGE_LOCKS = ON) 
) 

名稱也是唯一索引。進一步我有幾個進程以相當快的速度向這個表中添加數據。這些進程會使用一個存儲過程,看起來像:

ALTER PROC [dbo].[lg_Tag_Insert] 
    @Name varchar(255) 
AS 


DECLARE @ID int 
SET @ID = (select ID from Tag where [email protected]) 

if @ID is null 
    begin 
      INSERT Tag(Name) 
      VALUES (@Name) 

      RETURN SCOPE_IDENTITY() 

    end 
else 
    begin 
     return @ID 
    end 

我的問題是,除了是在並行數據庫設計新手等,似乎就是導致我偶爾會得到一個錯誤的競爭條件,我試圖在數據庫中輸入重複的密鑰(名稱)。錯誤是:

無法在具有唯一索引'IX_Tag_Name'的對象'dbo.Tag'中插入重複鍵行。

這是有道理的,我只是不知道如何解決這個問題。如果它的代碼我會知道如何鎖定正確的區域。 SQLServer是一個完全不同的野獸。

第一個問題是什麼是編碼'檢查,然後更新模式'的正確方法?看起來我需要在檢查過程中獲得一個排它鎖,而不是共享鎖,但我不清楚這是否是最好的方式。任何幫助正確的方向將不勝感激。提前致謝。

回答

0

正確的代碼是:

  • 在SP,與序列化交易最好運行
  • 做一個選擇到標記表中第一檢索一個id
  • 如果爲null,插入。

事務隔離將確保事務序列化。

緩存標籤客戶端,所以你不插入時,客戶端已經知道他們在那裏。性能障礙將是最小的。

看起來像你這樣做,所以唯一的問題可能是你的事務隔離級別。

你可以做的是:

  • 使用插入標記不同的連接。
  • 當你得到一個重複的錯誤,忽略它,查詢id,使用id。正如你在一個單獨的連接,並不重要。
1

我喜歡輸出參數(所以我編寫這種方式),但是這應該瓶坯最快,與命中桌子上的最少的:

ALTER PROC [dbo].[lg_Tag_Insert] 
    @Name varchar(255) 
    ,@ID int OUTPUT 
AS 
BEGIN TRY 
    SET @ID=NULL 
    INSERT Tag (Name) VALUES (@Name) 
    SET @ID=SCOPE_IDENTITY() 
END TRY 
BEGIN CATCH 
    SELECT @ID=ID from Tag where [email protected] 
END CATCH 

IF @ID IS NULL 
BEGIN 
    RETURN 1 
END 

RETURN 0 

GO 
0

我已經找到了最好的結果在表重插入將約束條件設置爲「忽略重複」,並在捕獲新插入物時讓模糊落到地板上。對於您的sproc,您可以完全消除該測試,並在插入後返回SCOPE_IDENTITY()或null。