T-SQL优化许多记录的DELETE

时间:2012-09-10 12:46:59

标签: sql sql-server tsql sql-delete

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

问题是,如果表中有这么多记录,那么这种删除可能需要花费很多时间,而且我想让它更快。

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

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

4 个答案:

答案 0 :(得分:14)

要扩展批量删除建议,我建议您更频繁地执行此操作(可能每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。完整数据库是fore table,你不能放松任何数据(即你可以将事务日志应用到将数据恢复到特定时间),SIMPLE数据库将用于这些大规模的高流量表,您可以在发生故障时允许数据丢失。

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

关于您的索引问题,如果您检查执行计划并查看任何“表扫描”,则必须将您的日期列编入索引,这将指示缺少索引。

您的日期列我假设DATETIME有一个约束,将DEFAULT设置为getdate()?

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

答案 1 :(得分:10)

日志问题可能是由于在trasaction中删除了记录的数量,更糟糕的是引擎可能会请求每个记录锁定一次(或者通过页面并不是那么糟糕)

这里最重要的一点是你如何确定要删除的记录,我假设您使用了一个日期时间字段,如果是这样,请确保您在列上有索引,否则它是顺序扫描这个表真的会惩罚你的过程。

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

  1. 如果您可以保证在删除时没有人会读取或写入,您可以将表锁定在独占模式并删除(这只需要一次锁定引擎)并释放锁定
  2. 您可以使用批量删除,您可以创建一个带有游标的脚本,该游标提供您要删除的行,然后开始transtaction并提交每个X记录(理想情况下为5000),这样您就可以保持事务的短路而不是很多锁
  3. 查看删除过程的查询计划,看看它显示的是什么,对大表的顺序扫描从来都不错。

答案 2 :(得分:1)

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

答案 3 :(得分:-2)

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

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

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

我用this link学习了这种技巧,解释了:

  

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

     

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

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