【问题标题】:Select top 1 with UPDLOCK and READPAST sets exclusive lock on the entire table使用 UPDLOCK 和 READPAST 选择 top 1 对整个表设置排他锁
【发布时间】:2010-09-10 09:42:12
【问题描述】:

我在存储过程中使用 UPDLOCK 和 READPAST sql 提示来实现一种表队列(我说排序是因为我选择了前 1500 个而不是前 1 个,并且我没有删除我选择它们后的行。有关更多详细信息,请参阅问题Return unlocked rows in a "select top n" query)。

我用一个简单的小表进行了一些测试,一切似乎都很好 - SP 的第二次调用不等待第一次调用结束,只是跳过第一次调用锁定的行,并返回下一个。不过,在我的真实桌子上,它有时只能工作......其他时候,第二个电话挂起并等待第一个电话结束。这是因为第一次调用会锁定整个表,而不仅仅是一些行。

这些是测试表和真实表的 SP:

实桌:

declare @temp as table (ID int primary key, timestamp datetime)

BEGIN TRANSACTION

insert into @temp
SELECT TOP 1 ID, getdate()
FROM subscription WITH (UPDLOCK, READPAST)
WHERE IsBeingProcessed = 0 

waitfor delay '00:00:10'

UPDATE subscription 
SET IsBeingProcessed = 1
from subscription
inner join @temp t on subscription.id = t.id

COMMIT TRANSACTION

select * from @temp t
inner join subscription s on s.id = t.id

测试表:

declare @temp as table (ID int primary key)

BEGIN TRANSACTION

insert into @temp(id)
select top 1 id
from test WITH (UPDLOCK, READPAST)
where msg like 'test'

waitfor delay '00:00:10'

UPDATE test 
SET timestamp = getdate()
from test
inner join  @temp t1 on test.id = t1.id 

COMMIT TRANSACTION

select * from test t
inner join @temp t1 on t.id = t1.id 

运行 sp_lock 我看到第一个 SP 在我的整个“真实”表上持有一个独占表锁,而第二个 SP 使用意图独占锁等待。对于我的“测试”表,我在索引和行 ID 上有一个更新锁,在两页上有两个意图更新锁,在整个表上有一个意图排他锁。

您是否知道什么可能导致“真实表”的整个表锁定?

也许我在那个表上有一些聚集索引,也许我缺少一些索引?我不知道。

我发布的示例非常简单——它有一个“前 1”,没有“排序依据”。我真正的选择将有一个“按 id 排序”和“前 1500 名”,可能是“前 3000 名”。这可能会使锁定问题更加严重。

非常欢迎任何想法!谢谢。

【问题讨论】:

  • 您使用的是什么版本的 SQL Server?
  • 没有ORDER BY时,top 1是什么意思?想想这个问题及其对您的索引的影响。请记住,真正的队列是先进先出的。队列查询中最具选择性的条件是 order by + top 组合。

标签: sql-server sql-server-2005 tsql concurrency


【解决方案1】:

我认为你的问题是索引。确保您的表上有正确的索引,并确保您的查询始终使用该索引。没有索引或不正确的索引会使 SQL Server 获取页锁或表锁,从而使整个模型无效。

【讨论】:

  • 我认为您很可能是对的,但我如何“确保”我拥有正确的索引?除了通过所有应用程序并看到一些额外或缺失的东西?这是一个长期运行的项目,有很多开发人员在搞乱它。在那张表上,我有 18 列、29 个统计信息和 10 个索引(1 个聚集)。
  • @Diana:在那种情况下,我认为你有一个严重的问题。使用READPAST 和UPDLOCK 时,您必须完全控制在该表上执行的实际查询。当有人向该表添加索引或使用不同的过滤器编写查询时,您的整个模型就会崩溃。我可以根据经验说。我的建议是防止应用程序直接访问该表,并使用一些专门设计的存储过程来访问该表。
【解决方案2】:

以下是您如何将表用作队列:SQL Server Process Queue Race Condition

总结:您缺少 ROWLOCK(在这种情况下将使用更多资源)

【讨论】:

    【解决方案3】:

    该表可能有太多列,应该拆分。也许它不是正确的第三或第四范式。

    另一件事是确保有一个专门针对您的查询的覆盖索引。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-25
      • 1970-01-01
      相关资源
      最近更新 更多