【发布时间】: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 锁。这让我觉得我们的“唯一”记录代码可能仍然会发生冲突。
问题
- 这些锁和锁的数量是否只是该类型负载的正常 SQL 操作?
- 在我们开始的 UPDATE 中,子查询是否会导致比 TOP(1) 更多的问题?我正在尝试确认我可以删除 ORDER BY 语句并删除额外的处理步骤。
- 不同的索引会有帮助吗?最初我想知道索引更新是否是导致锁定的可能原因,但现在我不确定了。
- 是否有更好或更有效的方法来获取唯一的 RowID?
- WITH (ROWLOCK) 导致的锁多于不使用它会导致的锁吗?这个想法是 ROWLOCK 只会锁定 1 条特定记录,并允许另一个 proc 更新另一条记录并选择而不锁定表或页面。
- 是否有人推荐任何工具来进行压力测试并同时运行 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