4

我在一个存储过程上的性能非常差,该存储过程对几个非常小的表进行了超过一百次(!)更新,并且似乎所有并发用户都在不断地相互阻塞。

计划在今年晚些时候对 proc 进行完全重写,但与此同时,我想看看我们是否可以通过强制对每个受影响的表进行行级锁定来缓解这个问题。

在 Sybase 上,您可以(或者至少可以在 2007 年左右)使用以下语句强制对表进行行级锁定:

alter table titles lock datarows

在 SQL Server 上,似乎获得相同效果的唯一方法是在每个更新或插入语句上使用 WITH (ROWLOCK)。即便如此,这也只是一个可以忽略的提示。

SQL Server 中是否有一种方法可以强制(或强烈支持)对给定表的所有更新进行行级锁定?

4

3 回答 3

10

首先要确保更新不是表扫描。换句话说,您确实拥有正确的索引(UPDATE 也需要索引......)。在您确保经过仔细考虑后,禁用所用索引上的页面锁定

ALTER INDEX ... WITH (ALLOW_PAGE_LOCKS = OFF, ALLOW_ROW_LOCKS = ON);

像在慢跑一桶 TNT 和一瓶凝固汽油弹时一样小心翼翼地进行这项操作……


附加信息(来自下面的评论):

  • 您可以禁用聚集索引上的页面锁,但不能禁用堆上的页面锁(因为它们是物理结构化的,并且无法在不锁定页面的情况下执行正确的锁层次结构)。

  • 锁升级是一个(相关但)不同的主题。仅当语句选择行级锁粒度并决定在执行期间升级到行集粒度级时,才会出现锁升级。确实,OP可能实际上是升级的受害者,从我阅读OP的方式来看,我认为更可能的原因只是缺少索引(即,由于扫描而预先选择了高锁定粒度,不会触发升级)。

  • 初始粒度级别是手头任务的引擎估计的结果。如果估计表明必须锁定大量行,那么它可能会选择页粒度,因为获取大量行锁通常是有问题的。缺少索引会触发扫描,扫描通常会选择页面粒度。

  • 升级也是从行/页到行集(对象)粒度。首先通过中间页面级别充满了并发问题,因此使用了“大锤”。

于 2013-01-28T09:09:18.937 回答
0

您可以使用它来禁用锁升级:

ALTER TABLE titles SET (LOCK_ESCALATION=DISABLE)

请参阅有关锁升级的文档

于 2013-01-28T09:23:24.393 回答
0
  1. 跟踪标志 1211
  2. 通过更改索引关闭页面级别锁定

这两个的组合将为您提供行级锁定。除非当然 sql 会决定直接进入表锁定 :) 还有 1224 标志,但它会忽略您的请求处于内存压力下,1211 不会。内存消耗可能会大幅增加,被警告并进行一些用户活动模拟(RML 或基准工厂)并运行 perfmon 进行观察。我认识过去做过的人。

于 2014-09-23T17:34:17.173 回答