SQL Server死锁的澄清?

3

阅读了这篇有趣的文章后,我有一些问题。

这张表格展示了死锁的情况:

enter image description here

T1在表t_lock1上持有所有c1=5行的X锁,而T2在表t_lock2上持有所有C1=1行的X锁。

现在,这些事务都想更新先前由对方锁定的行。这导致了死锁。

问题 #1

  • 事务是否获取了锁?我知道从表中读取是使用共享锁完成的,而写入表则使用排他锁完成(我谈论的是默认的锁定设置)。

因此,从这个例子中看来,事务也持有锁...这是正确的吗?

问题 #2

... T1在表t_lock1上持有所有c1=5行的X锁...

  • 依我之见,锁定不是按行进行的(尽管可以),但作者没有提到 - 那么为什么他说:在所有c1=5的行上?

抱歉离题了,你是如何做出带有边框的漂亮截图的? - Kermit
可能还有其他选择,但是snagit(商业/试用版)是一个选择。 - Christian.K
1
@njk http://www.faststone.org/FSCaptureDetail.htm - Royi Namir
2个回答

1
对于问题1:SQL Server使用U锁读取源表行,然后仅在符合更新条件的行上将它们转换为X锁。请注意,在读取许多行,然后将其过滤为要写入的行之间的区别。这两个集合的锁定方式不同。
由于查询中没有选择操作,因此只会获取U和X锁。在更新表时,不会获取S锁。这是一种启发式死锁避免方案。
问题2:锁定可以以不同的粒度进行,但对于低行数,通常是每行一次(并且可以强制执行)。也许作者假设C1上有索引,这意味着只需要读取和锁定C1 = 1的行。所有其他行都不会被触及。
如果没有索引,SQL Server确实会读取表的所有行,同时获取U锁,然后X锁定那些满足C1 = 1的行。作者确实提到只有C1 = 1的行被X锁定。

1
交易是否获得锁?
不是的。您执行的语句 - SELECTUPDATE 将获取锁定。根据您的事务隔离级别设置,持有(共享)锁定的时间(对于读取 SELECT)的持续时间会有所不同 - 这就是全部。共享锁通常只保留很短的时间,而更新和排他锁则一直保留到事务结束。事务可能会持有锁定 - 但并不是事务获取锁定...
*T1 在表 t_lock1 上的所有行中 c1=5 持有 X 锁定...*
我认为锁定不是按行进行的(尽管可以这样做,但作者没有提到),那么他为什么说:在所有 C1=5 的行上?

默认情况下,锁定是按行进行的。但是你为什么认为只有一行C1=5呢?可能会有多个 - 可能是数千个 - UPDATE语句将锁定UPDATE语句影响到的所有这些行


关于持续时间:如果我开始一项事务并执行选择操作,但不提交,锁定是否会永久存在?更新也是同样的情况吗? - Royi Namir
@RoyiNamir:任何事务都会在某个时候被提交或回滚。如果您忘记提交或回滚事务,那么SQL Server将在下次关闭时回滚它。因此,在SQL Server自动回滚事务之前可能需要相当长的时间,但是没有什么会永远被锁定 :-) - marc_s
我想我不理解共享锁这个东西。如果John从表中读取,并且同时Paul也读取该表(两者都运行select *),那么哪部分被锁定了(共享的)?是整个表还是每个人读取的行(或者甚至页面)?锁在哪里?John锁定他所读取的行还是他所读取的页面?如果它是共享的,我就看不到锁在哪里了... - Royi Namir
@RoyiNamir:在“正常”的READ COMMITTED隔离级别下,两个读操作将在读取时短暂地锁定它们正在读取的行。这只是防止在某人正在读取时进行同时更新或删除。两个共享锁是兼容的 - 因此,即使读操作同时发生,它们都将成功。锁定的对象是(或多行),一个进程正在读取。默认情况下,锁定总是在行级别上(锁定一个或多个行)。 - marc_s

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接