SQL并发测试更新问题

时间:2010-03-22 18:39:11

标签: database-design sql-server-2008 concurrency

我有一个SQLServer 2008数据库,其中有一个Tags表。标签只是一个id和一个名字。标签表的定义如下:

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 Name=@Name )

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

            RETURN SCOPE_IDENTITY()

    end
else
    begin
        return @ID
    end

我的问题是,除了作为并发数据库设计的新手之外,似乎有一种竞争条件导致我偶尔得到一个错误,我正在尝试将重复键(名称)输入到数据库中。错误是:

无法在对象'dbo.Tag'中插入具有唯一索引'IX_Tag_Name'的重复键行。

这是有道理的,我只是不确定如何解决这个问题。如果它代码我会知道如何锁定正确的区域。 SQLServer是一个完全不同的野兽。

第一个问题是对此'检查,然后更新模式'进行编码的正确方法是什么?看来我需要在检查期间获取排的锁,而不是共享锁,但我不清楚这样做的最佳方法。任何正确方向的帮助将不胜感激。提前谢谢。

3 个答案:

答案 0 :(得分: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 Name=@Name
END CATCH

IF @ID IS NULL
BEGIN
    RETURN 1
END

RETURN 0

GO

答案 1 :(得分:0)

正确的代码是:

  • 在SP中,最好使用可序列化的事务运行
  • 首先选择标签表以检索ID
  • 如果为null,请插入。

事务隔离将确保事务序列化。

缓存标记客户端,因此当客户端已经知道它们存在时,您不会插入。性能损失将是最小的。

看起来你这样做,所以唯一的问题可能是你的事务隔离级别。

你能做的是:

  • 使用单独的连接插入标记。
  • 当您收到重复错误时,请忽略它,查询ID,使用ID。因为你处于一个无关紧要的单独连接上。

答案 2 :(得分:0)