2010-03-22 80 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。