我需要在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给我该行。
如果我想念什么,谁能解释我?
答案 0 :(得分:2)
SQL Server符合对可序列化查询的严格定义。也就是说,必须有一个逻辑上可以生成的结果 IF 两个查询都以串行顺序运行-事务1在事务2开始之前完成,反之亦然。
这会导致某些效果可能与您预期的有所不同。在SQLPerformance.com处对可序列化隔离级别有一个很好的解释,它清楚地说明了此逻辑可序列化最终的含义。 (非常有用的网站,那个。)
对于上述查询,没有逻辑上的要求来防止第二个查询读取与第一个查询相同的行。无论查询以什么顺序运行,它们都将返回相同的数据而不修改它们。由于查询分析器可以识别出这一点,因此没有理由在数据上放置读取锁定。但是,如果其中一个查询对数据执行了更新,则(警告-这里的逻辑假设,因为我实际上不知道SQL Server如何处理此问题的内部原理),QA会对所选行设置更强的锁定。
TL; DR-SQL Server希望最大程度地减少阻塞,因此它使用逻辑分析来查看可序列化隔离级别需要哪些类型的锁,并且(尝试使用)达到所需的最小数量和强度的锁它的目标。
现在我们已经解决了这一点-我可以想到只有两种方法来锁定行,以便其他人都无法读取它:使用XLOCK + TABLOCK(锁定整个表-不建议这样做)或在您开始处理时在每行上都有某种形式的字段更新-例如SPID字段或Locked的位标志。当您在事务中更新它时,只有带有NOLOCK提示的SELECT才能读取它。
显然,这些都不是最优的。我建议使用“此行很忙-离开”标志,因为这可能是我对行(几乎)进行绝对锁定的方法。
答案 1 :(得分:0)
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
答案 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并向其中插入一行。