11

我有一张可以增长到数百万条记录的表(例如 5000 万条记录)。每 20 分钟删除超过 20 分钟的记录。

问题是如果表有这么多记录,这样的删除可能会花费很多时间,我想让它更快。

我不能做“截断表”,因为我只想删除超过 20 分钟的记录。我想在执行“删除”并过滤需要删除的信息时,服务器正在创建日志文件或其他东西,这需要很多时间吗?

我对吗?有没有办法停止任何标志或选项以优化删除,然后打开停止的选项?

4

4 回答 4

15

要扩展批量删除建议,我建议您更频繁地执行此操作(也许每 20 秒一次) - 批量删除很容易:

WHILE 1 = 1 
    BEGIN 
        DELETE TOP ( 4000 )
        FROM    YOURTABLE
        WHERE   YourIndexedDateColumn < DATEADD(MINUTE, -20, GETDATE()) 
        IF @@ROWCOUNT = 0 
            BREAK    
    END

您的插入在等待锁释放时可能会稍微滞后,但它们应该插入而不是错误。

不过,关于您的表,我希望在非常快的 raid 10 阵列/甚至分区上看到这么多流量的表-您的磁盘是否可以满足要求?您的事务日志是否与数据文件位于不同的磁盘上?- 他们应该是

编辑 1 - 回复您的评论

将数据库放入 SIMPLE 恢复:

ALTER DATABASE Database Name SET RECOVERY='SIMPLE'

这基本上会关闭给定数据库上的事务日志记录。这意味着如果发生数据丢失,您将需要丢失自上次完整备份以来的所有数据。如果您对此感到满意,那么在运行大型事务时这应该可以节省大量时间。(请注意,在事务运行时,日志记录仍以 SIMPLE 进行 - 以启用事务的回滚)。

如果您的数据库中有无法丢失数据的表,您需要让数据库处于完全恢复模式(即记录任何事务(并希望通过您的服务器维护计划刷新到 *.trn 文件)。因为我不过,在我的问题中说,没有什么能阻止你拥有两个数据库,1 个是 FULL,1 个是 SIMPLE。FULL 数据库将是你不能丢失任何数据的前表(即你可以应用事务日志来将数据恢复到一个特定的时间),而 SIMPLE 数据库将用于这些海量的高流量表,您可以在发生故障时允许数据丢失。

假设您每晚创建完整的 (*.bak) 文件并每半小时左右将日志文件刷新到 *.trn 文件,所有这些都是相关的)。

关于您的索引问题,如果您检查执行计划并看到任何“TABLE SCAN”,那么您的日期列必须被索引 - 这将表明缺少索引。

我认为您的日期列是 DATETIME,具有将 DEFAULT 设置为 getdate() 的约束?

您可能会发现通过将其替换为 BIGINT YYYYMMDDHHMMSS 然后将 CLUSTERED 索引应用于该列来获得更好的性能 - 但请注意,每个表只能有 1 个聚集索引,因此如果该表已经有一个,您需要使用非聚集索引。(如果您不知道,聚集索引基本上告诉 SQL 以该顺序存储信息,这意味着当您删除行 > 20 分钟时,SQL 可以按顺序删除内容,而不是从一页跳到另一页。

于 2012-09-10T14:00:35.870 回答
10

日志问题可能是由于在 trasaction 中删除的记录数,更糟糕的是,引擎可能会请求每条记录的锁(或者按页锁并不是那么糟糕)

这里的一件大事是您如何确定要删除的记录,我假设您使用日期时间字段,如果是这样,请确保您在列上有索引,否则它是对表的顺序扫描,这将真正惩罚您的过程.

根据用户的并发性和删除时间,您可以做两件事

  1. 如果可以保证删除的时候没有人去读或者写,可以排他锁表,然后删除(这样只需要引擎一把锁),然后释放锁
  2. 您可以使用批量删除,您将使用提供要删除的行的游标创建一个脚本,然后开始事务并提交每 X 条记录(理想情况下为 5000),因此您可以保持事务简短而不占用那么多锁

看一下删除过程的查询计划,看看它显示了什么,对大表的顺序扫描永远不会好。

于 2012-09-10T13:15:32.333 回答
1

不幸的是,出于这个问题的目的,幸运的是,为了 SQL Server 中数据库的一致性和可恢复性,将数据库置于简单恢复模式不会禁用日志记录。每个事务在将其提交到数据文件之前仍会被记录,唯一的区别是日志中的空间将在事务回滚或以简单恢复模式提交后立即释放(在大多数情况下) ,但这不会以某种方式影响 DELETE 语句的性能。

于 2014-02-19T18:57:29.427 回答
0

当我需要从具有 3 个索引和大量外键的大表中删除 70% 以上的行时,我遇到了类似的问题。

对于这种情况,我将所需的行保存在临时表中,截断原始表并重新插入行,例如:

SELECT * INTO #tempuser FROM [User] WHERE [Status] >= 600;
TRUNCATE TABLE [User];
INSERT [User] SELECT * FROM #tempuser;

我通过这个链接学习了这项技术,该链接解释了:

DELETE是一个完全记录的操作,如果出现问题可以回滚

TRUNCATE从表中删除所有行而不记录单个行删除

在文章中,您可以探索其他策略来解决删除许多记录的延迟问题,这对我有用

于 2017-06-27T21:03:40.590 回答