我认为自己是 Sql Server 锁定的初学者。
我的理解是,在使用 RCSI 时,Sql Server 不需要发出 S 锁,因为它使用行版本控制(在大多数情况下)。从http://technet.microsoft.com/en-us/library/jj856598(v=sql.110).aspx我们可以阅读以下关于 U 锁的信息:
用于可以更新的资源。防止在多个会话读取、锁定和稍后可能更新资源时发生的常见死锁形式。
知道了这一点,为什么Sql Server 需要发出U 锁(使用RCSI 时)?在我看来,Sql Server 可以简单地读取行,并在必须执行更新时直接请求 X 锁。
我一直在考虑这个的原因是因为我在更新同一个表的 2 个会话中遇到了死锁。像这样(为了清楚起见而简化):
第 1 节:
BEGIN TRAN
UPDATE t1 SET col1 = col1 + 100 WHERE col2 = value1
UPDATE t1 SET col1 = col1 + 100 WHERE col2 = value2
UPDATE t1 SET col1 = col1 + 100 WHERE col2 = value3
Run Code Online (Sandbox Code Playgroud)
第 2 节:
BEGIN TRAN
UPDATE t1 SET col1 = col1 …
Run Code Online (Sandbox Code Playgroud) sql-server deadlock locking isolation-level snapshot-isolation
维基百科说Read Committed 容易出现不可重复读取。但是,我可以简单地在我的事务中缓存第一个读取结果(制作快照)并释放数据库锁以让其他事务更新读取行。实际上,我以 RC 隔离为代价实现了重复读取,因此比可重复读取提供的性能更高。没有什么不好的事情发生,我可以像使用可重复读取隔离一样安全,对吗?不,我想对我读过的数据持有读锁可能有助于防止银行或预订预订中出现一些不良情况。哪个?
我正在寻找的是我的 RR 仿真失败的示例。我已经证明我可以通过在第一次读取访问时缓存数据项来使读取可重复。我想要一个我的模拟不够的例子。这种策略下读完立即释放锁有什么不好?
维基百科的文章和可重复阅读的名称在我看来暗示这就是我们所需要的。但我怀疑可重复读取通过使用共享锁,为了提供比简单读取一致性更重要的东西而牺牲了性能,因为后者可以通过简单的读取提交 + 缓存(快照)组合来实现。我猜读者锁在2PL 中的整个事务中持续存在,并且我们在多个读者/单作者中使用共享/读取锁,其目的不仅仅是在单个事务中提供可重复的读取,因此,Repeatable Read
是隐藏锁定事实的用词不当,它实际上牺牲了比一致性读取更大的性能。
https://habr.com/en/company/postgrespro/blog/467437/ 给出了以下丢失更新的示例:
例如,两笔交易将把同一账户的金额增加 100 欧元(? 是俄罗斯卢布的货币符号)。第一个事务读取当前值 (?1000),然后第二个事务读取相同的值。第一个交易增加了金额(这给出了 ?1100)并写入了这个值。第二个事务的行为方式相同:它获得相同的 ?1100 并写入该值。结果,客户损失了?100
我读了几次。但我不明白客户是如何丢失P100的。请解释。
当我有一个包含多个查询的事务时,我可以通过使用waitfor
和检查dm_tran_locks
.
但我不能将一个查询“暂停”一半。具体来说,我想知道这个查询将如何持有锁:
update my_table set column1=new_value
where column2=filter_value
Run Code Online (Sandbox Code Playgroud)
这会从一开始就采取更新锁定吗?还是会先获取共享锁,直到确定指定的行where
,然后请求 U 锁?
下面的两个会话尝试更新同一行的场景由 Paul White 提供。
场景一:
- 会话 1
U
在读取时获取基表行的锁。- 会话 2 阻塞,等待获取
U
基表中的同一行。- 会话 1 将值设置为 10 并提交。
- 会话 2 获取了它的
U
锁并且发现没有什么可做的,因为现在 Value != 0。结果:值设置为 10。
根据 Paul 的说法,会话 2 已经找到了需要更新的行,但它被阻止了。当阻塞消失并且会话 2 获得 U 锁时,它需要再次检查条件吗?我理解正确吗?对我来说这有点奇怪。会话找到了这一行,这是因为它评估了导致识别该行的过滤器。为什么在实际更新之前需要再次检查条件?