应使用什么类型的事务IsolationLevel来忽略插入但锁定所选行?

时间:2010-08-04 11:00:17

标签: sql sql-server transactions isolation-level

我有一个启动事务的进程,将记录插入Table1,然后调用长时间运行的Web服务(最多30秒)。如果Web服务调用失败,则回滚插件(这是我们想要的)。这是一个插入的例子(它实际上是多个表插入到多个表中,但我正在简化这个问题):

INSERT INTO Table1 (UserId, StatusTypeId) VALUES (@UserId, 1)

我有第二个进程从第一步查询Table1,如下所示:

SELECT TOP 1 * FROM Table1 WHERE StatusTypeId=2

然后为用户更新该行。当进程1运行时,Table1被锁定,因此进程2将不会完成,直到进程1完成,这是一个问题,因为在进程1完成其Web服务调用时引入了长延迟。

进程1只会插入一个StatusTypeId为1,它也是唯一插入Table1的操作。进程2只会查询StatusTypeId = 2.我想告诉进程2忽略对Table1的任何插入,但是锁定它选择的行。进程2的默认隔离级别等待太多,但我担心IsolationLevel.ReadUncommitted允许读取太多脏数据。我不希望两个用户运行进程2,然后意外地获取同一行。

是否有一个不同于ReadUncommitted的IsolationLevel表示忽略插入的行但是确保select锁定了所选的行?

2 个答案:

答案 0 :(得分:4)

修改:重新阅读此问题后,任何insert上的锁定都不应影响select下的任何READ COMMITTED,这可能是您的索引的问题。

但是,从您的评论和其他问题来看,您似乎只希望一个事务一次能够读取一行,这不是隔离级别所阻止的。

他们阻止

  • Dirty Read - 在可以回滚的事务中读取未提交的数据 - 发生在READ UNCOMMITTED中,在READ COMMITTEDREPEATABLE READ,{{1}中被阻止}

  • SERIALIZABLE - 在未提交的事务中读取时更新行,这意味着特定行的相同读取可能在事务中出现两次并产生不同的结果 - 发生在{{1} },Non Repeatable Reads。在READ UNCOMMITTEDREAD COMMITTED

  • 中被阻止了
  • REPEATABLE READ - 在未经通信的事务中读取时插入或删除行,这意味着在事务中可以出现两次相同的多行读取,并产生不同的结果,添加或丢失行 - 发生SERIALIZABLEphantom rowsREAD UNCOMMITTED中阻止了READ COMMITTED

答案 1 :(得分:4)

关于被插入阻止的SELECT,应该通过提供适当的索引来避免这种情况。

测试表。

CREATE TABLE Table1
(
UserId INT PRIMARY KEY,
StatusTypeId INT,
AnotherColumn varchar(50)
)
insert into Table1
SELECT number, (LEN(type)%2)+1, newid()
FROM master.dbo.spt_values
where type='p'

查询窗口一

BEGIN TRAN
INSERT INTO Table1 (UserId, StatusTypeId) VALUES (5000, 1)
WAITFOR DELAY '00:01';
ROLLBACK

查询窗口2(块)

SELECT TOP 1 * 
FROM Table1 
WHERE StatusTypeId=2 
ORDER BY AnotherColumn

但如果在添加索引后重试测试,则不会阻止CREATE NONCLUSTERED INDEX ix ON Table1 (StatusTypeId,AnotherColumn)

关于锁定Process 2的行,您可以使用以下内容(READPAST提示将允许2个并发Process 2事务处理开始处理不同的行,而不是一个阻塞另一个行。你可能会发现Remus Rusanu的this article相关

BEGIN TRAN

SELECT TOP 1 * 
FROM Table1  WITH (UPDLOCK, READPAST)
WHERE StatusTypeId=2
ORDER BY AnotherColumn

/*
Rest of Process Two's code here
*/
COMMIT