【问题标题】:Optimizing Delete on SQL Server在 SQL Server 上优化删除
【发布时间】:2010-10-31 14:58:46
【问题描述】:

sql server 上的Deletes 有时很慢,我经常需要优化它们以减少所需的时间。 我一直在谷歌上寻找有关如何做到这一点的提示,并且我发现了各种建议。 我想知道你最喜欢和最有效的驯服删除野兽的技术,以及它们的工作方式和原因。

到目前为止:

  • 确保外键有索引

  • 确保条件被索引

  • 使用WITH ROWLOCK

  • 销毁未使用的索引,删除,重建索引

现在,轮到你了。

【问题讨论】:

  • 给高级用户的问题:这个问题没有单一的答案,更像是一种知识库,而不是简单的问答。可能它可以变成一个社区维基? (如果我很了解 c.w. 的用途)
  • 我希望将其视为一个持续的总结。这篇文章对我非常有用,但花了几个小时来阅读这些建议。我提交了一个带有新摘要的编辑,但被拒绝了,正在等待第二次尝试是否通过:)
  • @xero 我回滚了您的编辑,您可能会在阅读 Should the community wiki police be shut down?What can we do to make Community Wiki better?meta.stackoverflow.com/a/266921 后标记管理员以引起注意(使用其他)
  • @bummi - 啊,在阅读了这些之后,我认为最好做一个 CW 回答而不是问题本身。我将为此发布一个摘要答案,并让社区参与其中。感谢您的跟进
  • 有助于减少删除时间的参数之一是 SET NOCOUNT ON;

标签: sql sql-server


【解决方案1】:

您可能会对以下文章快速有序删除操作感兴趣。

Performing fast SQL Server delete operations

该解决方案侧重于利用视图来简化为批量删除操作生成的执行计划。这是通过引用给定表一次而不是两次来实现的,这反过来又减少了所需的 I/O 量。

【讨论】:

【解决方案2】:

我对 Oracle 有更多的经验,但很可能这同样适用于 SQL Server:

  • 删除大量行时,发出表锁,这样数据库就不必做很多行锁了
  • 如果您从中删除的表被其他表引用,请确保其他表在外键列上有索引(否则数据库将对每个删除的行进行全表扫描 在另一张表上,以确保删除该行不会违反外键约束)

【讨论】:

  • 表锁会阻止对表的插入和更新,需要确保在其他事务开始超时之前删除速度很快。
  • dsum: true,但删除大量记录通常发生在没有其他活动的维护窗口中(例如晚上)。
【解决方案3】:

我想知道是否是垃圾收集数据库的时候了?您将行标记为删除,服务器稍后会在扫描期间将其删除。您不会希望每次删除都这样做 - 因为有时现在必须删除一行 - 但有时它会很方便。

【讨论】:

  • 我喜欢这个主意。您可以实现这一点,只需标记一点 To_be_delted,然后每隔一段时间运行一次查询以删除这些值。但我同意自动垃圾收集系统会很酷
  • 删除一行至少意味着三件事:a) 确保删除没有违反外键约束 b) 将该行占用的空间标记为“可用”。 c) 从该表的所有索引中删除该行。其中,a) 可能是最昂贵的(如果引用表在外键列上没有索引)但应该立即完成,所以你可以告诉用户“你不能删除这一行,它仍然参考”。 b) 可能很便宜,而 c) 通常没那么贵。因此,我不相信这个想法。
  • PostgreSQL 处理这样的删除,删除只是将受影响的行标记为已删除,一个名为“vacuum”的单独进程实际上释放了占用的空间。
【解决方案4】:

截至 2014 年 11 月 5 日的答案摘要

这个答案被标记为社区维基,因为这是一个不断发展的话题,有很多细微差别,但总体上可能的答案很少。

第一个问题是您必须问自己要优化的场景是什么?这通常是在 db 上使用单个用户的性能,或者在 db 上使用多个用户进行扩展。有时答案是完全相反的。

针对单用户优化

  • 提示TABLELOCK
  • 删除删除中未使用的索引,然后重新构建它们
  • 使用类似SET ROWCOUNT 20000(或其他任何内容,具体取决于日志空间)和循环(可能使用WAITFOR DELAY)进行批处理,直到你摆脱所有这些(@@ROWCOUNT = 0
  • 如果删除大部分表,只需创建一个新表并删除旧表
  • 对要删除的行进行分区,然后删除该分区。 [Read more...]

用于多用户优化

  • 提示行锁
  • 使用聚集索引
  • 设计聚集索引以在删除大块时最大限度地减少页面重组
  • 更新“is_deleted”列,然后在维护窗口期间进行实际删除

一般优化

  • 确保 FK 在其源表上具有索引
  • 确保WHERE 子句有索引
  • WHERE 子句中使用视图或派生表确定要删除的行,而不是直接引用该表。 [Read more...]

【讨论】:

    【解决方案5】:

    说实话,从表中删除一百万行与插入或更新一百万行一样严重。问题在于行集的大小,对此您无能为力。

    我的建议:

    • 确保表具有主键和聚集索引(这对所有操作都至关重要)。
    • 确保聚集索引能够在删除大量行时发生最小的页面重组。
    • 确保您的选择标准是 SARGable。
    • 确保当前所有外键约束都是可信的。

    【讨论】:

    • SARGable:在关系数据库中,如果 DBMS 引擎可以利用索引来加速查询的执行(使用索引查找),则查询中的条件(或谓词)被认为是 sargable ,不包括索引)。该术语源自 Search ARGument Able 的缩写。 (维基百科)
    【解决方案6】:

    (如果索引是“未使用的”,它们为什么会存在?)

    我过去使用的一个选项是分批完成这项工作。粗略的方法是使用SET ROWCOUNT 20000(或其他)并循环(可能使用WAITFOR DELAY)直到你摆脱它(@@ROWCOUNT = 0)。

    这可能有助于减少对其他系统的影响。

    【讨论】:

    • 但是通常发生的事情不仅仅是删除... .
    • 不要因为在删除中没有使用索引就删除它们!其他人正在使用数据库做其他事情!
    • 我应该指出,只有在不使用数据库的情况下完成长时间删除时才应该使用destroy-delete-rebuild,例如晚上,在批处理操作期间,当数据库是企业时db 只在白天使用。显然,销毁一个活跃的和使用过的数据库上的索引不是一个好主意。
    【解决方案7】:

    问题是你没有足够定义你的条件。 IE。你到底在优化什么?

    例如,系统是否因夜间维护而停机并且系统上没有用户?您是否要删除大部分数据库?

    如果脱机并删除了很大的百分比,则只需构建一个包含要保留的数据的新表、删除旧表并重命名可能是有意义的。如果删除一小部分,您可能希望在日志空间允许的情况下批量处理。这完全取决于您的数据库,但在重建期间删除索引可能会造成伤害或帮助——即使可能由于“离线”而可能。

    如果您在线,您的删除操作与用户活动发生冲突的可能性有多大(用户活动主要是读取、更新还是什么)?或者,您是否尝试优化用户体验或完成查询的速度?如果您要从其他用户经常更新的表中删除,则需要进行批处理,但要使用较小的批处理大小。即使您执行诸如表锁之类的操作来强制隔离,如果您的删除语句需要一个小时,那也没有多大用处。

    当您更好地定义您的条件时,您可以在此处选择其他答案之一。我喜欢 Rob Sanders 帖子中用于批处理的链接。

    【讨论】:

    • 感谢马特。好吧,我的问题很笼统,我在各种不同的场合都有缓慢的删除,这是一种收集人们可以在这个问题上分享的技巧的方法。
    【解决方案8】:

    如果您有很多外键表,请从链的底部开始向上处理。如果没有要级联删除的子记录,最终删除会更快并阻止更少的事情(如果我有大量子表,我不会打开它,因为它会降低性能)。

    批量删除。

    如果您有不再使用的外键表(您会惊讶于生产数据库最终会出现没有人会删除的旧表),请删除它们或至少断开 FK/PK 连接.如果表没有被使用,检查表的记录是没有意义的。

    不要删除 - 将记录标记为删除,然后从所有查询中排除标记的记录。这最好在数据库设计时设置。很多人使用它,因为它也是找回意外删除记录的最佳最快方法。但在现有系统中进行设置需要大量工作。

    【讨论】:

      【解决方案9】:

      我将在此添加另一个:

      确保您的事务隔离级别和数据库选项设置正确。如果您的 SQL 服务器设置为不使用行版本控制,或者您在其他查询上使用隔离级别,您将等待行被删除,您可能会在操作发生时设置自己的性能非常差.

      【讨论】:

        【解决方案10】:

        在您有一组非常具体的删除条件的非常大的表上,您还可以对表进行分区、切换分区,然后处理删除。

        SQLCAT 团队一直在对非常非常大量数据使用这种技术。我找到了一些对它的引用 here 但我会尝试找到更明确的东西。

        【讨论】:

          【解决方案11】:

          我认为,删除性能的最大陷阱是每行删除后的 sql 都会更新该行中任何列的所有相关索引。在批量删除之前删除所有索引怎么样?

          【讨论】:

            【解决方案12】:

            有删除,然后有删除。如果您将数据老化作为修剪作业的一部分,您将希望能够通过聚集键删除连续的行块。如果您必须对不连续的大容量表中的数据进行老化,这是非常非常痛苦的。

            【讨论】:

              【解决方案13】:

              如果 UPDATES 确实比 DELETES 快,您可以添加一个名为 DELETED 的状态列并在您的选择中对其进行过滤。然后在晚上运行一个执行实际删除操作的 proc。

              【讨论】:

                【解决方案14】:

                您是否已激活引用完整性的外键? 你有激活的触发器吗?

                【讨论】:

                  【解决方案15】:

                  简化 WHERE 子句中函数的任何使用!示例:

                  DELETE FROM Claims
                  WHERE dbo.YearMonthGet(DataFileYearMonth) = dbo.YearMonthGet(@DataFileYearMonth)
                  

                  WHERE 子句的这种形式需要 8 分钟才能删除 125,837 条记录。

                  YearMonthGet 函数用输入日期的年月组成一个日期,并设置day = 1。这是为了确保我们删除了基于年份和月份而不是日期的记录。

                  我将 WHERE 子句改写为:

                  WHERE YEAR(DataFileYearMonth) = YEAR(@DataFileYearMonth)
                  AND MONTH(DataFileYearMonth) = MONTH(@DataFileYearMonth)
                  

                  结果:删除这 125,837 条记录大约需要 38-44 秒!

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-03-04
                    • 1970-01-01
                    • 2012-05-31
                    • 2019-04-11
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多