【问题标题】:Purge job optimization清除作业优化
【发布时间】:2016-08-11 15:10:13
【问题描述】:

SQL Server 2008 R2 企业版

我有一个包含 3 个表的数据库,我将其保留时间为 15 天。这是一个非常活跃的日志数据库,大小约为 500 GB,除非被清除,否则每天消耗约 30 GB。我似乎无法赶上其中一张桌子,我落后了。该表有 2.2 亿行,每晚需要清除大约 10-12 百万行。我目前需要清除 3000 万行。由于竞争表锁的传入插入量,我只能在晚上运行此清除。我已确认所有内容均已正确编入索引,并已运行 Brent Ozars sp_Blitz_Index 以确认这一点。有什么方法可以优化我在下面做的事情吗?我为每个表运行相同的清除步骤。

  1. 删除并创建 3 个清除表:Purge_Log、Purge_SLogHeader 和 Purge_SLogMessage。

2.在清除表中插入行(每个表需要 5 分钟):

Insert Into Purge_Log
Select ID from ServiceLog 
where startTime <  dateadd (day, -15, getdate()  )

--********************************************* *******

Insert into Purge_SLogMessage
select serviceLogId from ServiceLogMessage 
where serviceLogId in ( select id from 
                       ServiceLog 
                       where startTime <  dateadd (day, -15, getdate() ))

--********************************************* *******

Insert into Purge_SLogHeader
Select serviceLogId from ServiceLogHeader
where serviceLogId in ( select id from 
                       ServiceLog 
                       where startTime <  dateadd (day, -15, getdate()  ))

插入之后,我运行以下每个表的差异:

SET ROWCOUNT 1000

delete_more:
delete from ServiceLog
where Id in ( select Id from Purge_Log)

IF @@ROWCOUNT > 0 GOTO delete_more
SET ROWCOUNT 0     

基本上有没有人看到我可以让这个过程运行得更快或者有不同的方法来完成它。我使查询尽可能简单,并且只有一个子查询。我使用了一个连接,执行查询计划说以这种方式完成它的时间是相同的。任何指导将不胜感激。

【问题讨论】:

  • 首先你应该停止像这样使用 ROWCOUNT。不推荐将其用于这样的删除。 msdn.microsoft.com/en-us/library/ms188774.aspx 您应该改为使用循环来进行批量删除。作为一种可行的编码实践,应该从你的想法中删除 GOTO。我不太明白为 Purge_Log 设置一个单独的表。为什么不直接删除开头的行而不是将它们全部复制到另一个表中?
  • 感谢您查看此内容。我将查看循环中的删除选项并重新评估 GOTO。我没有在开头删除行,因为在删除中包含 where 子句需要更多时间来搜索 2.2 亿行以查找日期。我将这些预取到新表中,然后对它们运行删除以提高删除速度。
  • 您也可以考虑分区,因为您使用的是企业版..查看此链接..mssqltips.com/sqlservertip/2780/…

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


【解决方案1】:

您可以对所有表使用此技术,首先在临时表中收集 ID,以避免在大量数据中一次又一次地扫描原始表。我希望它对您的所有表格都适用:

DECLARE @del_query VARCHAR(MAX)

/* 从 ServiceLog 表而不是 Purge_Log 中获取 ID,因为 Purge_Log 由于频繁清除而可能具有比预期更多的数据 */

IF OBJECT_ID('tempdb..#tmp_log_ids') IS NOT NULL DROP TABLE #tmp_log_ids
SELECT ID INTO #tmp_log_ids FROM ServiceLog WHERE startTime < DATEADD(DAY, -15, GETDATE())

SET @del_query ='
DELETE TOP(100000) sl
FROM ServiceLog sl 
INNER JOIN #tmp_log_ids t ON t.id = s1.id'
WHILE 1 = 1
BEGIN
    EXEC(@del_query + ' option(maxdop 5) ')
    IF @@rowcount < 100000 BREAK;
END 

SET @del_query ='
DELETE TOP(100000) sl
FROM ServiceLogMessage sl 
INNER JOIN #tmp_log_ids t ON t.id = s1.serviceLogId'
WHILE 1 = 1
BEGIN
    EXEC(@del_query + ' option(maxdop 5) ')
    IF @@rowcount < 100000 BREAK;
END

SET @del_query ='
DELETE TOP(100000) sl
FROM ServiceLogHeader sl 
INNER JOIN #tmp_log_ids t ON t.id = s1.serviceLogId'
WHILE 1 = 1
BEGIN
    EXEC(@del_query + ' option(maxdop 5) ')
    IF @@rowcount < 100000 BREAK;
END

【讨论】:

  • 将 where dateadd 子句添加到删除中会增加删除过程的大量时间,因为它必须读取的记录数量。
  • 是的,但是这个查询仍然比使用 IN 条件要快,因为我在经历了更多的技术之后,对一个包含大量数据的表使用了相同的查询,它证明比其他的更快。
  • 这适用于 ServiceLog 表,但问题是其他两个表不包含日期字段,我必须从 ServiceLog.ID = OtherTables.serviceLogId 的 ServiceLog 中提取它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-14
  • 1970-01-01
相关资源
最近更新 更多