SQL Server READPAST提示

时间:2009-12-11 22:43:07

标签: sql-server locking

我看到的行为看起来像是在数据库本身上设置了READPAST提示。

摩擦:我不认为这是可能的。

我们有表foo(id int主键标识,名称varchar(50)not null unique);

我有几个线程,基本上

id = select id from foo where name = ?
if id == null
    insert into foo (name) values (?)
id = select id from foo where name = ?

每个线程负责插入自己的名称(没有两个线程尝试同时插入相同的名称)。客户端是java。

READ_COMMITTED_SNAPSHOT为ON,事务隔离专门设置为READ COMMITTED,使用Connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

症状是,如果一个线程正在插入,则另一个线程无法看到它的行 - 甚至是在应用程序启动之前提交给数据库的行 - 并尝试插入,但会获得重复键异常来自名称上的唯一索引。

在这里扔我一块骨头?

4 个答案:

答案 0 :(得分:1)

你处于错误的隔离级别。请记住快照隔离级别会发生什么。如果一个事务正在进行更改,则没有其他并发事务会看到该事务。期。其他事务只会在您提交后才会看到您的更改,但前提是它们在您提交后才会启动。解决方案是使用不同的隔离级别。在事务中包装您的语句并设置SET TRANSACTION LEVEL SERIALIZABLE。这将确保您的其他并发事务就像它们都是串行运行一样工作,这就是您在这里想要的。

答案 1 :(得分:0)

听起来你没有把select和insert插入到一个事务中?

作为解决方案,您可以:

insert into foo (col1,col2,col3) values ('a','b','c')
where not exists (select * from foo where col1 = 'a')

在此之后,如果可以检查是否插入了行,@@rowcount将为1。

答案 2 :(得分:0)

SELECT SCOPE_IDENTITY()

应该在这里诀窍......

再加上像之前提到的海报一样的交易。

答案 3 :(得分:0)

这个故事的寓意在我的博客文章"You can't hold onto nothing"中得到了充分的解释,但这个版本的简短版本就是你要使用HOLDLOCK提示。我使用的模式:

INSERT INTO dbo.Foo(Name)
SELECT TOP 1
    @name AS Name
FROM (SELECT 1 AS FakeColumn) AS FakeTable
WHERE NOT EXISTS (SELECT * FROM dbo.Foo WITH (HOLDLOCK)
                  WHERE Name=@name)
SELECT ID FROM dbo.Foo WHERE Name=@name