缓慢批量插入具有许多索引的表

时间:2009-04-15 10:14:10

标签: sql sql-server indexing bulkinsert

我尝试将数百万条记录插入到包含20个以上索引的表中。

在上一次运行中,每100,000行需要超过4小时,查询在3½天后被取消...

您对如何提高速度有任何建议。

(我怀疑很多索引都是原因。如果您也这么认为,如何在操作之前自动删除索引,然后再次创建相同的索引?)

额外信息:

  • 索引使用的空间大约是单独使用数据空间的4倍
  • 插入数据包含在每100,000行的事务中。

状态更新:

接受的答案帮助我加快了速度。

4 个答案:

答案 0 :(得分:42)

您可以禁用和启用索引。请注意,禁用它们可能会产生不必要的副作用(例如,具有重复的主键或唯一索引等),只有在重新启用索引时才会发现这些副作用。

--Disable Index
ALTER INDEX [IXYourIndex] ON YourTable DISABLE
GO

--Enable Index
ALTER INDEX [IXYourIndex] ON YourTable REBUILD
GO

答案 1 :(得分:7)

这听起来像是一个数据仓库操作。 在插入之前删除索引并在之后重建它们是正常的。

重建索引时,首先构建聚簇索引,然后反过来删除它。他们应该都有100%的填充因子。

代码应该是这样的

if object_id('Index') is not null drop table IndexList
select name into Index from dbo.sysindexes where id = object_id('Fact')

if exists (select name from Index where name = 'id1') drop index Fact.id1
if exists (select name from Index where name = 'id2') drop index Fact.id2        
if exists (select name from Index where name = 'id3') drop index Fact.id3
.
.
BIG INSERT

RECREATE THE INDEXES

答案 2 :(得分:4)

如另一个答案所述,禁用索引将是一个非常好的开始。

  

每100,000行4小时   [...]   插入数据包含在每100,000行的事务中。

你应该考虑减少数量,服务器必须在事务中保持大量状态(因此可以回滚),这(与索引一起)意味着添加数据非常困难。< / p>

为什么不将每个insert语句包装在自己的事务中?

另请查看您正在使用的SQL的性质,您是在每个语句(和网络往返)中添加一行,还是添加多行?

答案 3 :(得分:3)

在这些情况下,经常会建议禁用然后重新启用索引。我对这种方法有疑问,因为:

(1)应用程序的DB用户需要通常不应具有的模式更改权限。 (2)首先选择的插入方法和/或索引模式可能不是最优的,否则重建完整的索引树不应该比一些体面的批量插入更快(例如,客户端一次发出一个插入语句,导致成千上万的服务器往返;或者在聚簇索引上选择不当,导致常量的索引节点拆分。)

这就是我的建议看起来有点不同的原因:

  • 增加ADO.NET BatchSize
  • 明智地选择目标表的聚簇索引,以便插入不会导致聚簇索引节点拆分。通常,标识列是一个不错的选择
  • 让客户端首先插入临时堆表(堆表没有任何聚簇索引);然后,发出一个大的“insert-into-select”语句将所有临时表数据推送到实际目标表中
  • 应用SqlBulkCopy
  • 通过选择批量记录恢复模型来减少事务日志记录

您可以在this article中找到更详细的信息。