【问题标题】:improve sql delete performance and reduce log file and tempDB size?提高 sql 删除性能并减少日志文件和 tempDB 大小?
【发布时间】:2012-07-07 06:40:58
【问题描述】:

我有一个庞大的数据库,它每天处理电子邮件流量。在系统中,每天都需要删除一些旧邮件:

Delete from EmailList(nolock) 
WHERE EmailId IN ( 
    SELECT EmailId 
    FROM Emails 
    WHERE EmailDate < DATEADD([days], -60, GETDATE())
)

它可以工作,但问题是:它需要很长时间才能完成,因此日志文件变得非常大。日志文件大小每天增加超过 100GB。

我想我们可以把它改成

Delete from EmailList(nolock) 
WHERE EXISTS ( 
    SELECT EmailId 
    FROM Emails 
    WHERE (Emails.EmailId = EmailList.EmailId) AND 
        (EmailDate < DATEADD([days], -60, GETDATE()))
)

但除此之外,我们还能做些什么来提高性能。最重要的是,减少日志文件的大小?

  • EmailId 已编入索引。

【问题讨论】:

  • 在 DELETE 上使用 NOLOCK 绝对没有意义。 NOLOCK 不是 DML 操作的有效提示。
  • "EmailId 已编入索引。"在哪个表中?可以发一下查询计划吗?
  • 你的数据库的恢复模式是什么?以及您减少日志大小的策略是什么?
  • @Aaron,我有点惊讶 SQL 语句甚至在其中正确解析。我也在想同样的事情。
  • 我从 DBA 那里复制了脚本,实际上我不知道 nolock 是从哪里来的。代码是c#,里面没有nolock。对困惑感到抱歉。恢复模式很简单。坦率地说,我什至没有想到 tempDB 可以在短时间内增加这么多。

标签: sql-server database tempdb


【解决方案1】:

[编辑]:

关于@TomTom 的评论: 如果您有可用的 SQL Server Enterprise 版本,您应该使用表分区

如果不是这样,我的原始帖子可能会有所帮助:


[原帖]

删除大量数据总是很困难的。我遇到了同样的问题,我采用了以下解决方案:

根据您的要求,这将不起作用,但也许您可以从中获得一些想法。

不要使用 1 个表,而是使用具有相同架构的 2 个表。创建一个同义词(我假设您使用的是 MS SQL 服务器)指向“两个表的活动表(活动的意思是,这是您当前写入的表)。将此同义词用于应用程序中的插入,或者应用程序可以不使用同义词,而是每 x 天写入一次就更改表。

每隔 x 天,您可以截断旧的/不活动的表,然后重新创建同义词以针对截断的表(如果您使用同义词解决方案),因此您每次都有效地对数据进行分区。

您必须同步活动表的开关。我通过为应用程序使用共享 App-lock 和在更改同义词时使用 Exclusive Applock(== 在切换过程中阻止写入应用程序)完全自动化了这一点。

如果更改应用程序的代码不是一个选项,请考虑使用相同的原则,但不是写入同义词,而是可以创建一个视图而不是触发器(插入操作将插入“活动”分区)。触发代码需要使用上面提到的 Applock 之类的东西进行 syhcnronize(以便在切换过程中写入)。

我的解决方案稍微复杂一些,因此我目前无法在此处发布代码,但它对于高负载应用程序没有问题,并且切换/清理过程是完全自动化的。

【讨论】:

  • ?在这样的 scnario 中按日期删除大 amonts 是微不足道的,并且可以在亚秒内完成,没有任何问题。为此开发了一些特殊的技术,即表分区。任何处理大量数据的人都应该知道如何解决这个问题。
  • 谢谢。这听起来像是一个上帝的主意。稍后会检查它。现在想弄清楚是否有更简单的方法可以达到目的,所以没必要对c#代码做太多改动。不过,感谢您指出一个有希望的解决方案。再次感谢。
  • @TomTom 是的,有技术,但是如果您使用的是 MS Sql,则需要企业版,这在我的情况下不可用(其他数据库系统也有类似的概念,但我认为这是 MSSQL服务器而不是企业版 - 这可能是错误的)。但这是一个很好的观点 - 如果有可用的技术,请使用它们。
  • 那么,抱歉,您没有大量数据。您甚至没有非常大量的数据。当然,小型数据库在这里有问题,但在我的世界里,“休”是“十亿行或更多”,或者更准确地说是“每天 1 亿行,存储几年”。它大于非常大,并且 VLDB 是一个防御大小。
  • 是的,这与我的经验相去甚远,是的,我假设在这些情况下,您有预算和基础设施来处理这类问题。但我仍然认为在某些情况下我的帖子可能会有所帮助。正如你所提到的 - 巨大/大是相对值....
【解决方案2】:

您是否尝试过按日期进行分区,那么您可以只删除您不再感兴趣的日子的表格版本。给定一个“hugh”数据库,您肯定会运行企业版的 SQL Server(毕竟,hugh 比非常大)并且具有表分区。

【讨论】:

  • 感谢提及表分区。会检查它。但是,该程序由不同的用户使用,我不能假设他们都使用企业版。可能需要先检查 C# 代码中的数据大小和 sql 版本,然后使用不同的方式删除。但似乎应该有更好/直接的方法来做到这一点,尽管我可能是错的。谢谢。
  • 啊——不。站起来说你没有“大量数据”就足够了。 Hugh 听起来比现在数百 GB 的 VLDB 更大 - 我的最后一个数据库是 21000 GB ;) 你不能用普通标准版运行它。
  • 好吧,够公平的。那么,对于非高数据数据库,有什么建议?批量删除?我需要在批次之间手动做一些事情吗?即截断?或者,让 sql server 自动执行?谢谢
  • 删除 - 但不是全部,每个事务只有 10.000 到 50.000 个项目,在一个循环中。并且日志会上升 - 忍受它,这就是它的完成和制作方式,temptc 不会采用这种方法。运行循环,直到你只删除 0 个项目,然后所有的东西都消失了。
  • 我尝试使用循环删除。数据库恢复mdoe很简单,batch size是2000,删除时间要长很多。正常吗?如果我增加批量大小,我猜它会更快。但是,我的问题是:对于批量大小,什么 # 是一个不错的选择?因此,它会减慢太多并将日志文件大小保持在合理范围内。如果我先检查将删除多少行,然后将批处理设置为 # 的 10%,会更好吗?
【解决方案3】:

见过

GetDate()-60

样式语法的性能比

好很多
DATEADD([days], -60, GETDATE()))

尤其是在日期列上有索引的情况下。我和几个 DBA 同事花了相当多的时间试图理解为什么它会表现更好,但结果是在布丁中。

考虑到我认为您必须删除的记录量,您可能需要考虑的另一件事是分批删除,例如 1000 或 10000 条记录。这可能会加快删除过程。

【讨论】:

  • [code]WHILE EXISTS (SELECT TOP 1 * FROM table WHERE ) BEGIN SET ROWCOUNT 1000 DELETE Table WHERE SET ROWCOUNT 0 END 这能正常工作吗?我相信批量删除会截断日志,但它会减慢系统速度(因为它需要在每批中截断)?我开发了c#代码,没想到会被用来处理海量数据。感谢您的任何建议。
  • 如果我批量删除,是否需要在批次之间手动截断?还是数据库会自动完成?
  • 截断什么?当您删除时,您会截断表格。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-17
  • 1970-01-01
相关资源
最近更新 更多