【问题标题】:Trouble with SQL Server locksSQL Server 锁的问题
【发布时间】:2015-06-08 14:44:42
【问题描述】:

我遇到了一个问题,即 SQL Server 导致我们的主表上出现大量锁定(95 到 150)。它们通常是持续时间短的锁定,持续时间不到 3 秒,但如果可能的话,我想消除它们。我们还注意到,通常没有块,但有时我们会遇到块似乎“级联”的情况,然后整个系统会大大减慢。

背景

我们有多达 600 台虚拟机处理数据,并且我们在 SQL 中加载了一个表,以便我们可以监控任何停止的记录和标记为完成的记录。在处理过程中,此表中通常有 200,000 到 1,000,000 条记录。

我们正在努力实现的目标

我们正在尝试获取下一条可用记录(状态 = 0)。然而,由于存储过程可能同时有多个命中,我们试图确保每个 VM 获得唯一的记录。这很重要,因为每条记录的处理时间在 1.5 到 2.5 分钟之间,我们希望尽可能地干净。

到目前为止我们的思考过程

UPDATE TOP (1) dbo.Test WITH (ROWLOCK)
SET Status = 1, 
    VMID = @VMID, 
    ReadCount = ReadCount + 1,
    ProcessDT = GETUTCDATE()
OUTPUT INSERTED.RowID INTO @retValue
WHERE Status = 0

这个更新给我们带来了一些关于锁的问题,所以我们稍微重新处理了这个过程,并将 where 更改为子查询,以从表中返回前 1 个 RowID(主键)。这似乎有助于让事情运行得更顺畅一些,但我们偶尔会再次在数据库中超载。

UPDATE TOP (1) dbo.Test WITH (ROWLOCK)
SET Status = 1, 
    VMID = @VMID, 
    ReadCount = ReadCount + 1,
    ProcessDT = GETUTCDATE()
OUTPUT INSERTED.RowID INTO @retValue
-- WHERE Status = 0
WHERE RowID IN (SELECT TOP 1 RowID FROM do.Test WHERE Status = 0 ORDER BY RowID)

我们发现,表中有大量状态 1 和 2 记录会导致速度变慢。我们认为它来自对 Status 列的表扫描。我们添加了以下索引,但它无助于解决锁定问题。

CREATE NONCLUSTERED INDEX IX_Test_Status_RowID
ON [dbo].[Test] ([Status])
INCLUDE ([RowID])

UPDATE 后的最后一步,我们使用返回的 RowID 选择出详细信息:

SELECT 'Test' as FileName, *, @Nick as [Nickname] 
FROM Test WITH (NOLOCK)
WHERE RowID IN (SELECT id from @retValue)

锁的种类

大多数块是 LCK_M_U 和 LCK_M_S,我希望使用 UPDATE 和 SELECT 查询。我们偶尔也有 1 或 2 个 LCK_M_X 锁。这让我觉得我们的“唯一”记录代码可能仍然会发生冲突。

问题

  1. 这些锁和锁的数量是否只是该类型负载的正常 SQL 操作?
  2. 在我们开始的 UPDATE 中,子查询是否会导致比 TOP(1) 更多的问题?我正在尝试确认我可以删除 ORDER BY 语句并删除额外的处理步骤。
  3. 不同的索引会有帮助吗?最初我想知道索引更新是否是导致锁定的可能原因,但现在我不确定了。
  4. 是否有更好或更有效的方法来获取唯一的 RowID?
  5. WITH (ROWLOCK) 导致的锁多于不使用它会导致的锁吗?这个想法是 ROWLOCK 只会锁定 1 条特定记录,并允许另一个 proc 更新另一条记录并选择而不锁定表或页面。
  6. 是否有人推荐任何工具来进行压力测试并同时运行 100 个查询以测试任何潜在的解决方案?

抱歉所有问题,只是想确保我尽可能清楚地了解我们的流程和我们遇到的问题。

提前感谢您提供任何见解,因为这对我们来说是一个非常令人沮丧的问题。

硬件

我们在具有 24 GB RAM 的双 Xeon CPU 上运行 SQL Server 2008 R2。所以我们应该有足够的能力来完成这个过程。

【问题讨论】:

  • 看来您正在尝试做一个简单的生产者-消费者。你有没有想过使用一些消息队列,比如 MSMQ 或 RabbitMQ?这些过程的实现相当复杂。
  • 对于负载测试,您可以尝试 Adam Machanic datamanipulation.net/sqlquerystress 的 SQL Query Stress
  • 检查不同更新选项的统计 IO 输出可能很有用,这应该有助于解决最佳查询
  • 您可能应该有充分的理由,但我只是想知道您为什么不使用 IDENTITY 列来获取唯一 ID?
  • 还有一件事要尝试。当您尝试为列定位一组相当独特的值时,例如您的状态,您可以创建一个filtered index。在您的情况下,过滤器将是 status = 0。这将增加您使用该索引的机会并减少读取它的次数。

标签: sql-server sql-server-2008-r2


【解决方案1】:

该问题的最佳解决方案似乎是创建一个具有标识的单独表,并使用插入中的@@IDENTITY 来确定要处理的下一行。到目前为止,这已经解决了我在压力测试中的所有锁定问题。感谢所有指出我正确方向的人!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多