新连接的READPAST锁定问题

时间:2012-10-03 09:24:01

标签: sql-server vb.net locking

我有一个存储过程,如下所示:

USE [DBName]
GO
/****** Object:  StoredProcedure [dbo].[ProcName]    Script Date: 10/03/2012 12:10:16 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[ProcName](@filter bigint, @n int)
AS
DELETE FROM TableName WITH (READPAST, UPDLOCK, ROWLOCK) OUTPUT(DELETED.ColumnName2)
WHERE TableName.ID in (select top (@n) ID from TableName where TableName.ColumnName1 = @filter)

此过程返回ColumnName2的第一个@n值(这些值表示的记录将从表中删除)。我从vb方法使用这个存储过程,它通常运行良好。但是,有时,由于我不知道的原因,它会引发异常:

You can only specify the READPAST lock in the READ COMMITTED or REPEATABLE READ isolation levels.

我的vb方法调用此存储过程,如果它没有获得足够的值,则它会生成新值并调用此存储过程。重复此过程,直到有足够的值。从本质上讲,TableName就像一个队列,我的vb方法运行良好,但是,有时会抛出上面提到的异常。是什么原因可以解决我的问题?

我试图仅为调用我的存储过程的代码启动一个新连接,但无效,因为异常再次被抛出。我不知道可能是什么解决方案,尽管我已阅读以下文章:

http://www.red-gate.com/messageboard/viewtopic.php?t=13614

.NET READPAST lock error when calling a stored procedure

http://support.microsoft.com/kb/981995

http://blogs.technet.com/b/claudia_silva/archive/2011/08/08/replication-error-quot-you-can-only-specify-the-readpast-lock-in-the-read-committed-or-repeatable-read-isolation-levels-quot-generated-when-altering-published-table-columns.aspx

提前感谢您的帮助,

LajosÁrpád。

2 个答案:

答案 0 :(得分:10)

这应该可以解决您的问题

ALTER PROCEDURE [dbo].[ProcName](@filter bigint, @n int)
AS
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
DELETE FROM TableName WITH (READPAST, UPDLOCK, ROWLOCK) OUTPUT(DELETED.ColumnName2)
WHERE TableName.ID in (select top (@n) ID from TableName where TableName.ColumnName1 = @filter)

由于您使用的是需要此设置的READPAST,因此您可以在SP中明确设置它,因为它是安全范围的。

  

如果您在存储过程中发出SET TRANSACTION ISOLATION LEVEL或   触发器,当对象返回控制时,隔离级别被重置   到调用对象时的有效级别。

参考:SET TRANSACTION ISOLATION LEVEL

至于它为什么会发生,可能的原因是SQL调用之间的连接池和混合事务隔离级别。

答案 1 :(得分:4)

首先,DELETE不正确。为了达到你想要的效果,你需要这样写:

WITH T AS (
   SELECT TOP (@n)
    ColumnName2
   FROM TableName WITH (READPAST, UPDLOCK, ROWLOCK) 
   WHERE ColumnName1 = @filter)
DELETE FROM T
OUTPUT DELETED.ColumnName2;

此重写消除了UPDATE与顶级ID扫描之间的竞争条件。为了工作,WHERE子句必须是SARGable(即ColumnName1必须是索引),否则你最终会以扫描 结束,并且READPAST没有任何帮助。有关详细信息,请参阅Using Tables as Queues

现在回到你的问题:是什么导致了不同的隔离级别?好吧,你的代码中有些东西你没有在这里发布。如果我冒险尝试,它必须是使用默认构造函数构造的TransactionScope对象。请参阅Using new TransactionScope() Considered Harmful,了解这是如何发生的以及为什么不好。链接的文章还包含解决方案:使用事务范围的显式构造函数,即接受TransactionOptions的显式构造函数,在该{{3}}上明确指定所需的隔离级别。