【问题标题】:SQL Server READPAST hintSQL Server READPAST 提示
【发布时间】:2010-03-25 02:57:27
【问题描述】:

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

问题:我认为这是不可能的。

我们有表 foo (id int primary key identity, name 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 );

症状是,如果一个线程正在插入,另一个线程看不到它的行——即使是在应用程序启动之前提交到数据库的行——并尝试插入,但得到一个重复键异常来自 name 的唯一索引。

在这里给我一根骨头?

【问题讨论】:

  • 一些关于打包交易的建议 -- 很抱歉我忘了说明我们正在这样做(通过 Connection#setAutocommit(false))
  • 还有一点 - 我们删除 name 的唯一索引,一切正常。 (另外,我们不会得到重复的数据。)但这不可能是真正的解决方案。

标签: sql-server locking


【解决方案1】:

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

【讨论】:

  • 这是一个好主意,尽管我不确定它与我们所看到的情况有何不同:应用程序启动之前数据库中的行不再可见。换句话说,我试图查看其行的事务不是其他线程的事务,它们是长期提交的事务中的行。
  • 那么我认为你有一个更大、更根本的问题。我不认为你认为正在发生的事情实际上正在发生。如果您确定事务已提交并且读者看不到已提交的行,则这不是提示或隔离级别的情况。它要么被某人以某种方式更改,要么实际上从未被提交。如果您确实需要查看发生了什么,请尝试使用像 Red-Gate 之类的事务日志查看器。
  • @Dave - 谢谢,你说得对。我们有一个更深层次的问题,我以错误的方式解释了这个问题。我们无法读取某些行的值,但我们可以读取其他行的值。 “坏”值是由单个线程处理的值只是巧合。我们看到的问题类似于下面链接的问题。删除唯一索引“修复”了问题。当然,我们不能没有任何约束地留下唯一索引,但我们又要搬家了:social.msdn.microsoft.com/Forums/en/sqldatabaseengine/thread/… 谢谢!
【解决方案2】:

听起来您没有将 select 和 insert 包装到事务中?

作为一种解决方案,您可以:

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

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

【讨论】:

  • 好建议,但初始选择失败,即使对于自应用程序启动之前就已在数据库中的行也是如此。我们正在寻找可以从 select 语句中隐藏行的方法。
  • @dg:行不能从选择语句中隐藏
【解决方案3】:
SELECT SCOPE_IDENTITY()

应该在这里解决问题...

加上之前提到的海报中提到的交易。

【讨论】:

  • 肯定会更有效,但插入时发生异常,因为第一个选择没有找到它正在寻找的行。如果插入成功,SCOPE_IDENTITY 将起作用。
【解决方案4】:

这个故事的寓意在我的博文"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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-03
    • 2011-04-25
    • 1970-01-01
    • 2011-03-14
    • 2016-04-20
    • 2011-06-29
    • 1970-01-01
    相关资源
    最近更新 更多