考虑以下设置:
create table Tabl
(
ClustInd int,
NonClustInd int,
NonClustInd_Addon int,
OtherCol int
)
create clustered index CLI on Tabl (ClustInd)
create nonclustered index NCLI on Tabl (NonClustInd) include(NonClustInd_Addon)
go
--just generating & inserting 2^4*4 = 65536 records:
;with h(a) as (select 0 union all select 0),
h1(a) as (select h1.a from h h1, h h2, h h3, h h4),
h2(a) as (select h1.a from h1, h1 h2, h1 h3, h1 h4),
r(a) as (select row_number() over (order by a) from h2)
insert into Tabl(ClustInd, NonClustInd, NonClustInd_Addon, OtherCol)
select a, a*10 - 10000, 60 + a*5, a*3 from r
现在如果你在一个会话中运行它:
declare @g int
while 1 = 1
--Using "OtherCol" to force the NCLI -> CLI -> Tbl lookup
select @g = OtherCol from Tabl where NonClustInd = 477210
这在另一个:
while 1 = 1
update Tabl
--Updating NonClustInd_Addon to force the updating of the NCLI and CLI indexes
set NonClustInd_Addon = case when NonClustInd_Addon = 1 then 2 else 1 end
where NonClustInd = 477210
“select”语句将使用update语句死锁。
它们死锁的原因是因为select语句首先锁定“NCLI”索引,然后锁定“CLI”索引。并且更新语句 - 首先是“CLI”索引,然后是“NCLI”。显然这最终会导致僵局。
有没有办法打败这个僵局?也许有一种方法可以控制索引被锁定的顺序(因为,例如,如果SQL锁定CLI,然后是NCLI进行更新和选择,则此死锁将永远不会出现)。 感谢。
PS:其他信息:
在更新期间,存在以下锁:
在选择期间:
死锁前的秒数:
答案 0 :(得分:3)
使用快照隔离或read committed snapshot
。这通常很容易实施,而且负面后果往往是值得的。它通过将只读事务完全排除在外,简化了您的生活。它们不再锁定或阻塞。
如果你能做到这一点,问题就解决了。
答案 1 :(得分:1)
通过使用'NOLOCK',我们可以避免这种僵局......试试这样......
declare @g int
while 1 = 1
select @g = OtherCol from Tabl WITH(NOLOCK) where NonClustInd = 477210
答案 2 :(得分:0)
作为强力解决方案,您可以在表格中添加一列:lockColumn int not null default 0
在两次交易开始时,请致电update tab1 set lock = lock + 1 where NonClustInd = whatever