我正在尝试解决 SQL Server 2008 R2 的问题并且正在抓紧救命稻草。
鉴于数据位于 B 树的叶节点中,并且也存在于索引中,有人可以在单个UPDATE
语句中读取多个值吗?
想象一个进程更新一行:
--Change status from Pending -> Waiting
BEGIN TRANSACTION
UPDATE Transactions SET Status = 'Waiting'
WHERE TransactionID = 12345
COMMIT TRANSACTION
是否有可能在此期间BEGIN TRANS; UPDATE; COMMIT
另一个进程可以读取新值和旧值?
我想知道这一点,因为更新不会仅在一个地方更改值。该值将存在于多个地方:
- 聚集索引的叶节点
- IX_Transactions_1
- IX_Transactions_2
- IX_Transactions_3
- IX_Transctions_4
- IX_Transctions_5
- ...
- IX_Transactions_n
如果更新开始,它必须更新所有这些位置的值。如果另一个进程使用索引执行计划来查找值会发生什么:
- IX_Clustered:
等待中 - IX_Transactions_1:
等待中 - IX_Transactions_2:
等待中 - IX_Transactions_3:待定
- IX_Transactions_4:待定
- IX_Transactions_5:待定
- ...
- IX_Transactions_n:待定
在那一刻,如果发出一个查看聚集索引中的值的查询,它将找到Waiting。
但是,如果查询使用IX_Transactions_4
它,它将找到一个值Pending
。
取消记录锁定机制
内部的锁定顺序没有记录,但我假设 SQL Server 在聚集和非聚集索引上采用共享锁:
- IX_Clustered:共享
- IX_Transactions_1:共享
- IX_Transactions_2:共享
- IX_Transactions_3:共享
- IX_Transactions_4:共享
- IX_Transactions_5:共享
然后将这些锁升级为更新锁:
- IX_Clustered:
共享更新 - IX_Transactions_1:
共享更新 - IX_Transactions_2:
共享更新 - IX_Transactions_3:
共享更新 - IX_Transactions_4:
共享更新 - IX_Transactions_5:
共享更新
此时,另一个进程仍然可以读取这 5 个索引中的值;因为选择与更新锁兼容。
然后更新继续。如果我们及时拍一张照片:
- IX_Clustered:
共享更新独占 -> 值已更改 - IX_Transactions_1:
共享更新独占 -> 值已更改 - IX_Transactions_2:
共享更新独占 - IX_Transactions_3:
共享更新 - IX_Transactions_4:
共享更新 - IX_Transactions_5:
共享更新
另一个进程不能再从具有排他锁的项目中读取值,但它仍然可以从仍然具有更新锁的项目中读取值(共享锁与更新锁兼容)
当然除非那不是发生的事情
这仅在 SQL 仅根据需要提升为独占时才有效。相反,如果它是这样的:
- IX_Clustered:
SUIXX -> 值已更改 - IX_Transactions_1:
SUIXX -> 值已更改 - IX_Transactions_2:
SUIXX - IX_Transactions_3:
SUIX - IX_Transactions_4:
SUIX - IX_Transactions_5:
SUIX
我不能再打字了;我继续下去的意志被粉碎了。就像我说的,我正在抓住救命稻草。
您可以使用跟踪标志
1200
打印出锁定信息和3916
日志记录信息。(均为无证 AFAIK)对于以下测试设置
然后运行以下两次(因为第二次运行不包含编译期间获得的无关锁)
这将返回以下输出
在更新聚集索引键(在页面上
1:166
)之前,不会对非聚集索引 () 采取任何锁定,所以是的,这是可能的。LOP_MODIFY_ROW
1:127
以上是一个狭窄的(每行)更新计划。
对于宽(每个索引)计划,每个索引都会依次更新。可能按照下面的计划进行干预排序操作。
这将使机会窗口变宽,但是,虽然事务可能会从尚未更新的索引中读取“旧”值,但读取已提交的事务不可能读取值的“新”版本,直到事务已提交。
读取已提交的语句当然有可能读取相同值的两个不同的已提交版本。
在一次连接运行中
然后在另一场比赛中
对我来说,大约有 50% 的时间第二次查询返回具有不同
B
值的行,因为值在两次搜索之间发生变化T
。