26

我尝试将数百万条记录插入具有 20 多个索引的表中。

在最后一次运行中,每 100.000 行花费了 4 多个小时,并且查询在 3½ 天后被取消......

您对如何加快速度有什么建议吗?

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

额外信息:

  • 索引使用的空间大约是数据单独使用的空间的 4 倍
  • 插入每 100.000 行包装在一个事务中。

状态更新:

接受的答案帮助我让它更快。

4

4 回答 4

43

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

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

--Enable Index
ALTER INDEX [IXYourIndex] ON YourTable REBUILD
GO
于 2009-04-15T10:23:44.333 回答
9

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

重建索引时,首先构建聚集索引,反之,最后将其删除。它们都应该具有 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
于 2009-04-15T10:30:52.587 回答
4

正如另一个答案所指出的那样,禁用索引将是一个非常好的开始。

每 100.000 行 4 小时 [...] 插入每 100.000 行包含在一个事务中。

您应该考虑减少数量,服务器必须在事务中维护大量状态(因此可以回滚),这(连同索引)意味着添加数据非常困难。

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

还要查看您使用的 SQL 的性质,您是每条语句(和网络往返)添加一行,还是添加很多?

于 2009-04-15T10:31:02.397 回答
3

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

(1) 应用程序的 DB 用户需要模式更改权限,通常不应该拥有这些权限。(2) 选择的插入方法和/或索引模式可能一开始就不是最优的,否则重建完整的索引树不应该比一些体面的批量插入更快(例如,客户端一次发出一个插入语句,导致数以千计的服务器往返;或者对聚集索引的选择不当,导致索引节点不断分裂)。

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

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

您可能会在本文中找到更详细的信息。

于 2011-10-09T19:00:13.490 回答