【问题标题】:Transactions getting slower in SQL ServerSQL Server 中的事务变慢
【发布时间】:2018-01-20 08:13:12
【问题描述】:

我们在 SQL Server 中使用完全恢复模式。我们有一个从临时表合并到最终表的工作。临时表包含数百万行。决赛桌也很大,有数百万行。我们分批合并 10,000 行。

下面给出单个批次的伪代码:

BEGIN TRANSACTION

DELETE TOP 10000 * 
FROM <Staging Table> 
OUTPUT deleted.* INTO @TableVariable

MERGE INTO <Final Table> 
USING @TableVariable

COMMIT TRANSACTION

问题是,对于每个新批次,批次操作都会变慢。当我们重新启动服务器时,批处理再次变得更快。事务也没有写入磁盘,并且需要很长时间才能插入磁盘。我们怀疑这是事务日志的问题。当我们减小批处理大小时,会发生更多事务,并且批处理速度会更慢。

有没有办法提高这种批量删除和合并操作的性能?您是否建议使用CHECKPOINT 强制进入完全恢复模式?

【问题讨论】:

  • 只是一个想法;将“巨大”表连接到表变量可能不是最佳途径,因为它总是会将表变量视为包含 1 行。
  • 我明白了。当我们进行批处理时,我们在表变量中每 10,000 行加载一次,并将其用于合并。这是第一次,随着每个批次的进展,它的速度越来越快。我的问题是如何避免未来批次的减速。

标签: sql-server performance transactions transaction-log recoverymodel


【解决方案1】:

我们所做的是,我们没有强制执行 CHECKPOINT 流程,而是在 WHILE LOOP 中引入了人为延迟,这样事务就不会受到限制。

我们能够克服由于 SQL Server 环境中的事务限制而导致的内存不足问题。我们在临时表中有数百万行。引入的这 10,000 个批处理和延迟确保我们不会使服务器超载。有人访问服务器。


DECLARE @RowCount INT;

SET @RowCount = (SELECT COUNT(*) FROM StagingTable);

WHILE (@RowCount > 0)
BEGIN

    BEGIN TRANSACTION

    DELETE TOP 10000 * 
    FROM <Staging Table> 
    OUTPUT deleted.* INTO @TableVariable

    MERGE INTO <Final Table> 
    USING @TableVariable

    COMMIT TRANSACTION

    WAITFOR DELAY '00:00:10'; --artificially introduce 10 seconds delay

    SET @RowCount = (SELECT COUNT(*) FROM StagingTable);

END 

【讨论】:

  • 这是很久以前的项目。这个问题缺少答案。我现在已经给出了答案。我们有一个场景,我们需要从临时表中删除数据并合并到最终表中。所以,我们不得不去 DELETE ,然后是 MERGE。
  • 是的。我的意思是,通过避免多余的更新,您可以改进合并操作,因为事务日志上写入的信息更少。使用WHEN MATCHED AND source.col1 &lt;&gt; target.col2 AND source.col2 &lt;&gt; target.col2 ....。注意可以为空的列。你可以使用(s.col1 &lt;&gt; t.col1 OR s.col1 IS NULL AND t.col1 IS NOT NULL OR s.col1 IS NOT NULL AND t.col1 IS NULL)
【解决方案2】:

通常可以通过避免多余的更新来改进合并操作。如果由于目标行和源行相等而没有要更新的内容,则不要更新该行。这在大多数行没有更改的情况下非常有效,因为 SQL Server 在事务日志上写入的信息要少得多。

为避免对合并操作进行多余的更新,请编写如下合并语句:

MERGE INTO target AS t
USING source AS s
ON t.id = s.id
WHEN MATCHED 
  AND ((t.col1 <> s.col1 
       OR t.col1 IS NULL AND s.col1 IS NOT NULL
       OR t.col1 IS NOT NULL AND s.col1 IS NULL)
  OR (t.col2 <> s.col2 
       OR t.col2 IS NULL AND s.col2 IS NOT NULL
       OR t.col2 IS NOT NULL AND s.col2 IS NULL)
  OR (t.col2 <> s.col3 
       OR t.col3 IS NULL AND s.col3 IS NOT NULL
       OR t.col3 IS NOT NULL AND s.col3 IS NULL))
THEN UPDATE SET
  col1 = s.col1, col2 = s.col2, col3 = s.col3
WHEN NOT MATCHED BY TARGET THEN 
    INSERT (id, col1, col2, col3)
    VALUES (s.id, s.col1, s.col2, s.col3);

【讨论】:

  • 我认为您应该在此处使用 OR 条件,因为其中一列可能已更改。在我们的例子中,我们有近 100 多列。因此,我们在 MATCHED 时进行了简单的合并操作。在少量列的情况下可以避免多余的合并。但是,对于大列,我认为事务的长度可能会更长。
  • 你是对的。改为或。显然,如果列数较多,则需要更多 CPU,但在您尝试衡量之前,您不会知道这是否值得。
  • 同意。在这个项目中,我们采用了延迟方法,它解决了我们的问题。正如我告诉你的那样,这是很久以前的项目,昨天我意识到,这个问题缺少答案。所以,更新了方法,我们尝试了。我赞成您的回答,因为这是一个好方法。不幸的是,我们没有想到这一点,无法尝试。
猜你喜欢
  • 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
相关资源
最近更新 更多