如何在SQL Server中实现可序列化的隔离级别

时间:2018-11-20 23:18:34

标签: sql sql-server transactions isolation-level

我需要在SQL Server中实现可序列化的隔离级别,但是我已经尝试了很多方法,但没有得到。

我需要在一个事务中锁定1行(是否锁定整个表都没有关系)。因此,另一笔交易甚至无法选择该行(不读取)。

我尝试的最后一件事:

对于交易1:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN

SELECT code FROM table1 WHERE code = 1

-- Here I select in another instance the same row

COMMIT TRAN

对于交易2:

BEGIN TRAN
SELECT code FROM table1 WHERE code = 1
COMMIT TRAN

我希望事务2等待事务1提交操作,但是事务2给我该行。

如果我想念什么,谁能解释我?

3 个答案:

答案 0 :(得分:2)

SQL Server符合对可序列化查询的严格定义。也就是说,必须有一个逻辑上可以生成的结果 IF 两个查询都以串行顺序运行-事务1在事务2开始之前完成,反之亦然。

这会导致某些效果可能与您预期的有所不同。在SQLPerformance.com处对可序列化隔离级别有一个很好的解释,它清楚地说明了此逻辑可序列化最终的含义。 (非常有用的网站,那个。)

对于上述查询,没有逻辑上的要求来防止第二个查询读取与第一个查询相同的行。无论查询以什么顺序运行,它们都将返回相同的数据而不修改它们。由于查询分析器可以识别出这一点,因此没有理由在数据上放置读取锁定。但是,如果其中一个查询对数据执行了更新,则(警告-这里的逻辑假设,因为我实际上不知道SQL Server如何处理此问题的内部原理),QA会对所选行设置更强的锁定。

TL; DR-SQL Server希望最大程度地减少阻塞,因此它使用逻辑分析来查看可序列化隔离级别需要哪些类型的锁,并且(尝试使用)达到所需的最小数量和强度的锁它的目标。

现在我们已经解决了这一点-我可以想到只有两种方法来锁定行,以便其他人都无法读取它:使用XLOCK + TABLOCK(锁定整个表-不建议这样做)或在您开始处理时在每行上都有某种形式的字段更新-例如SPID字段或Locked的位标志。当您在事务中更新它时,只有带有NOLOCK提示的SELECT才能读取它。

显然,这些都不是最优的。我建议使用“此行很忙-离开”标志,因为这可能是我对行(几乎)进行绝对锁定的方法。

答案 1 :(得分:0)

根据documentation

  

SERIALIZABLE指定以下内容:

     
      
  • 语句无法读取已被其他事务修改但尚未提交的数据。
  •   
  • 在当前事务完成之前,没有其他事务可以修改当前事务读取的数据。
  •   
  • 其他事务无法插入键值落入由行中任何语句读取的键范围内的新行。   当前交易,直到当前交易完成。
  •   

如果您未使用事务1内的INSERTUPDATEDELETE对数据进行任何更改,则SQL将在读取完成后释放共享锁。

您可能想尝试添加一个表命中,以防止在事务1结束之前释放行锁。

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN

SELECT code 
FROM table1 WITH(ROWLOCK, HOLDLOCK)
WHERE code = 1

COMMIT TRAN

答案 2 :(得分:0)

也许您可以用这样的技巧来解决这个问题?

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
UPDATE someTableForThisHack set val = CASE WHEN val = 1 THEN 0 else 1 End
SELECT code from table1.....
COMMIT TRANSACTION

因此,您创建了一个表someTableForThisHack并向其中插入一行。