![](/img/trans.png)
[英]Locking and reference counting with SQL ISOLATION LEVEL SERIALIZABLE
[英]How to implement Serializable Isolation Level in SQL Server
我需要在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给了我该行。
如果我错过了什么,有人可以向我解释吗?
SQL Server遵循严格的可序列化查询定义。 也就是说, 如果两个查询都以串行顺序运行,则必须有一个逻辑上可以生成的结果-事务1在事务2开始之前完成,反之亦然。
这会导致某些效果可能与您预期的有所不同。 在SQLPerformance.com上对Serializable隔离级别有很好的解释,它清楚地说明了此逻辑可序列化最终的含义。 (非常有用的网站,那个。)
对于上述查询,没有逻辑上的要求来防止第二个查询读取与第一个查询相同的行。 无论查询以什么顺序运行,它们都将返回相同的数据而不修改它们。 由于查询分析器可以识别出这一点,因此没有理由在数据上放置读取锁定。 但是,如果其中一个查询对数据执行了更新,则(警告-这里的逻辑假设,因为我实际上不知道SQL Server如何处理此问题的内部原理),QA会对所选行设置更强的锁定。
TL; DR-SQL Server希望最大程度地减少阻塞,因此它使用逻辑分析来查看可序列化隔离级别需要哪些类型的锁,并且(尝试)使用达到其目标所需的最小数量和强度的锁。
现在我们已经解决了这一点-我可以想到只有两种方式来锁定行,以便其他人都无法读取它:使用XLOCK + TABLOCK(锁定整个表-不建议这样做)或进行一些操作每行开始时都会更新的字段形式-例如SPID字段或Locked的位标志。 当您在事务中更新它时,只有带有NOLOCK提示的SELECT才能读取它。
显然,这些都不是最优的。 我建议使用“此行很忙-离开”标志,因为这可能是我对行(几乎)进行绝对锁定的方法。
根据文档 :
SERIALIZABLE指定以下内容:
- 语句无法读取已被其他事务修改但尚未提交的数据。
- 在当前事务完成之前,没有其他事务可以修改当前事务已读取的数据。
- 在当前事务完成之前,其他事务不能插入键值将落入当前事务中任何语句读取的键范围内的新行。
如果您未使用事务1内的INSERT
, UPDATE
或DELETE
对数据进行任何更改,则SQL将在读取完成后释放共享锁。
您可能要尝试添加一个表命中,以防止在事务1结束之前释放行锁。
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
SELECT code
FROM table1 WITH(ROWLOCK, HOLDLOCK)
WHERE code = 1
COMMIT TRAN
也许您可以使用类似的技巧来解决此问题?
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并向其中插入一行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.