TABLOCK与TABLOCKX


77

TABLOCKTABLOCKX http://msdn.microsoft.com/zh-cn/library/ms187373.aspx和有什么区别,它指出TABLOCK共享锁TABLOCKX是排他锁,而独占锁是。是第一个也许仅仅是索引锁吗?共享锁的概念是什么?


8
它指出这TABLOCK是一个共享锁,允许多个客户端同时将数据加载到表中。排他锁没有。
Lieven Keersmaekers,

@Lieven使用制表符时,互斥性被排除在锁定之外,在锁定中,数据表在保留页存储周期中被压缩。
Roel 2014年

Answers:


105

最大的不同是,TABLOCK将尝试获取“共享”锁和TABLOCKX排他锁。

如果您正在进行交易,并且在表上获得排他锁,则EG:

SELECT 1 FROM TABLE WITH (TABLOCKX)

没有其他进程能够获取表上的任何锁,这意味着所有尝试与表进行对话的查询都将被阻塞,直到事务提交为止。

TABLOCK仅获取共享锁,如果事务隔离为READ COMMITTED(默认),则在执行语句后释放共享锁。如果您的隔离级别更高,例如:SERIALIZABLE,则共享锁将保留到事务结束。


共享锁是hmmm,是共享的。含义2个事务如果都在表上持有S或IS锁(通过TABLOCK),则它们都可以同时从表中读取数据。但是,如果transaction A在表上持有共享锁,transaction B则在释放所有共享锁之前,将无法获取排他锁。在msdn上了解有关哪些锁与哪些锁兼容的信息。


这两个提示都会导致数据库绕过采取更多的粒度锁定(例如行或页面级锁定)。原则上,更精细的锁可以使您获得更好的并发性。因此,例如,一个事务可以同时从两个事务更新表中的第100行,并更新另一行1000 (使用页锁会比较棘手,但请跳过该行)。

通常,粒度锁定是您想要的,但是有时您可能希望减少db并发以提高特定操​​作的性能并消除死锁的机会。

通常,在某些情况下,您将不使用TABLOCKTABLOCKX除非您绝对需要它。


8
例如,这些极端情况之一就是您最初创建表并用填充数据的情况INSERT INTO。在这种情况下,排他锁可以为您带来很大的性能提升。
Abel

1
这意味着所有尝试与该表进行对话的查询都将被阻止,直到事务提交为止。这不是真的 你仍然可以用NOLOCK或事务隔离级别读取未提交的读取
宝TA-脚趾

这似乎在sql server 2016上不起作用?
Frederik Gheysels

5

关于mssqlcity的很多旧文章试图解释锁的类型:

共享锁用于不更改或更新数据的操作,例如SELECT语句。

当SQL Server打算修改页面时,使用更新锁,然后在实际进行更改之前将更新页面锁升级为独占页面锁。

排他锁用于数据修改操作,例如UPDATE,INSERT或DELETE。

它没有讨论的是Intent(基本上是这些锁类型的修饰符)。意向(共享/独占)锁是比实际锁具有更高级别的锁。因此,例如,如果您的事务在一行上具有X锁,则它在表级别上还将具有IX锁(这将阻止其他事务尝试在表的更高级别上获得不兼容的锁(例如,架构)修改锁定),直到您的交易完成或回滚为止。


“共享”锁的概念非常简单-多个事务可以对同一资源具有共享锁,而只有单个事务可以具有互斥锁,并且互斥锁会阻止任何事务获取或持有共享锁。


持锁的时间长短有区别吗?TABLOCK保持到语句结束。如果还指定了HOLDLOCK,则共享表锁将保留到事务结束。但是TABLOCKX怎么样
Carlo V. Dango

@Carlo-排他锁始终保持到交易结束。
Damien_The_Unbeliever

1
@Carlo,所有共享锁都在读已提交的事务中的语句末尾释放,如果要保留锁,则需要提示或更高的隔离级别。tablockx会抓住X锁。
山姆·萨弗隆

3

这更像是TABLOCK对我不起作用而TABLOCKX对我有用的示例。

我有2个会话,它们都使用默认的(READ COMMITTED)隔离级别:

会话1是一个显式事务,它将数据从链接的服务器复制到数据库中的一组表中,并且需要花费几秒钟来运行。[示例,它删除了问题]会话2是一条插入语句,它只是将行插入到会话1不会对其进行更改的表中。[示例,它插入答案]。

(实际上,在会话1运行其事务时,有多个会话同时向表中插入多个记录)。

会话1必须查询会话2插入的表,因为它不能删除依赖于会话2添加的条目的记录。[示例:删除未回答的问题]。

因此,在执行会话1并尝试插入会话2时,会话2每次都会陷入死锁。

因此,会话1中的delete语句可能看起来像这样:DELETE tblA from tblQ LEFT JOIN tblX on ... LEFT JOIN tblA a ON tblQ.Qid = tblA.Qid WHERE ... a.QId为NULL且...

死锁似乎是由于在会话2 [3、4、5,...,n]尝试插入tblA时查询tblA之间的争执引起的。

就我而言,我可以将会话1事务的隔离级别更改为SERIALIZABLE。当我这样做时:事务管理器已禁用其对远程/网络事务的支持。

因此,我可以按照此处接受的答案中的说明进行处理:事务管理器已禁用对远程/网络事务的支持

但是a)首先我不愿意将隔离级别更改为SERIALIZABLE-据说它会降低性能,并且可能会产生其他我没有考虑的后果,b)不明白为什么这样做突然导致事务处理跨链接服务器工作时出现问题,并且c)不知道通过启用网络访问可能会打开哪些漏洞。

在一个非常大的事务中似乎只有6个查询引起了麻烦。

因此,我阅读了有关TABLOCK和TabLOCKX的文章。

我不清楚这些差异,也不知道两者是否都能奏效。但似乎会。首先,我尝试了TABLOCK,但似乎没有任何区别。竞争的会议产生了同样的僵局。然后我尝试了TABLOCKX,没有更多的死锁。

因此,在六个地方,我需要做的就是添加一个WITH(TABLOCKX)。

因此,会话1中的delete语句可能看起来像这样:DELETE tblA from tblQ q LEFT JOIN tblX x on ... LEFT JOIN tblA a WITH(TABLOCKX)ON tblQ.Qid = tblA.Qid WHERE ... a.QId IS NULL和...


2
欢迎来到stackoverflow!感谢您发布第一个答案。我认为,如果您删除个人参考并保留基本事实,那么此答案对其他人将更有用。保持良好的工作!
卡托
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.