Sql server sql server删除速度因索引而急剧降低

Sql server sql server删除速度因索引而急剧降低,sql-server,stored-procedures,optimization,sql-delete,Sql Server,Stored Procedures,Optimization,Sql Delete,我正在运行一个归档脚本,它根据输入日期从一个大的(~50m记录数据库)中删除行。日期字段是表上的聚集索引,因此也是我应用条件语句的目的 我在while循环中运行这个delete,一批尝试1000到100000条记录。不管批量大小,它的速度惊人地慢;大约每分钟有10000条记录被删除。看看执行计划,有很多时间花在“索引删除”上。表中大约有15个字段,其中大约10个字段上有某种索引。有没有办法绕过这个问题?我甚至不知道为什么每次删除索引都要花这么长时间,有人能解释一下这里到底发生了什么吗?这是我的执

我正在运行一个归档脚本,它根据输入日期从一个大的(~50m记录数据库)中删除行。日期字段是表上的聚集索引,因此也是我应用条件语句的目的

我在while循环中运行这个delete,一批尝试1000到100000条记录。不管批量大小,它的速度惊人地慢;大约每分钟有10000条记录被删除。看看执行计划,有很多时间花在“索引删除”上。表中大约有15个字段,其中大约10个字段上有某种索引。有没有办法绕过这个问题?我甚至不知道为什么每次删除索引都要花这么长时间,有人能解释一下这里到底发生了什么吗?这是我的执行计划示例:

(序列指向Delete命令)


这个数据库是实时的,并且经常被插入到数据库中,这就是为什么我不愿意使用复制和截断方法来调整大小。这里是否缺少其他选项?

更多的解决方法,但是您是否可以向表中添加
IsDeleted
标志,并将其更新为
1
,而不是删除行?您需要修改
选择
更新
以使用此标志


然后,您可以安排在下班时间删除或归档这些记录。

考虑到这是在生产中,实施这些记录需要一些工作,但是如果您使用的是SQL Server 2005/2008,您应该调查并将表转换为分区表,那么可以非常快地删除旧数据。它是为“滚动窗口”类型的效果而设计的,可以防止大规模的删除占用表/进程


不幸的是,由于该表已投入生产,将其迁移到这种技术将需要一些T-SQL编码、知识和一个周末来升级/迁移它。一旦到位,尽管任何现有的选择和插入都会无缝地与之对抗,但分区维护和添加/删除是您需要t-sql来控制进程的地方。

我支持@NickLarsen在评论中提出的建议。看看你有没有,把它们扔了。这可以减少这些索引删除的开销,这可能足以使操作更加及时


另一个更激进的策略是删除所有索引,执行删除,然后为现在更小的数据集快速重新创建索引。这并不一定会中断服务,但同时可能会使查询速度慢得多。虽然我不是Microsoft SQL Server专家,但您应该对我的建议持保留态度。

假设表中的每条记录都有5条索引记录

现在每个删除本质上是5个操作

此外,还有一个聚集索引。请注意,聚集索引删除时间非常长?(10倍)比其他指数长?这是因为您的数据正在重新组织,每个记录都已删除


我建议至少删除那个索引,进行大规模删除,而不是重新应用。对delete和insert进行索引操作的成本很高。一次重建可能要快得多。

从聚集索引中删除10k条记录+5条非聚集记录肯定不会花费1分钟。听起来你的IO子系统非常慢。以下各项的价值是什么:

  • 平均磁盘秒/写入
  • 平均磁盘秒/读取
  • 平均磁盘写入队列长度
  • 平均磁盘读取队列长度
在操作涉及的每个驱动器上(包括日志驱动器!)。如果将索引放置在单独的文件组中,并将每个文件组分配给其自己的LUN或磁盘,则可以确定哪些索引问题更大。此外,日志刷新可能是一个主要瓶颈。SQL Server在这里没有太多的控制权,如何加快速度完全掌握在您自己的手中。这段时间不是花在CPU周期上,而是花在等待IO完成上,您需要一个针对所需负载进行校准的IO子系统

为了减少IO负载,您应该考虑使索引更窄。首先,确保聚集索引是最窄的。然后,确保非聚集索引不包含零星未使用的大型列(我已经看到了…)。通过启用,可能会获得重大收益。最后,检查中的索引使用统计信息,看看是否有索引对axe有利

如果你不能大大减少IO负载,你应该尝试将其拆分。将文件组添加到数据库,在单独的文件组上移动大索引,将文件组放置在单独的IO路径(不同的轴)上


对于将来的常规删除操作,最好的替代方法是使用分区切换,使所有索引与聚集索引分区对齐,到期时,只需删除最后一个分区即可进行闪电般的删除。

是否完全需要所有这些索引?如果你不使用索引,你应该去掉它们。我刚刚和我们网站上的主要程序员谈过,听起来我们可能不再需要所有的索引了。当我们对表进行一些查询时,它更常用作日志类型表。在再次运行删除脚本之前,我可能会尝试删除一些插入。好吧,我打算每天在非工作时间定期运行此脚本以保持数据库修剪(它会删除任何超过2年的记录),但初始运行速度太慢,目前需要大约4个小时才能完成,这比那些想把服务器绑起来的权力还多。谢谢你的建议!在这种情况下,只需一次删除较小的批(例如,1000个),以便从最终用户的角度看不会对服务器负载产生明显的影响,并在每个循环之间以30-60秒的延迟重复此操作。然后让它运行直到完成。可能需要一周或更长时间