【问题标题】:Slow bulk insert for table with many indexes具有许多索引的表的慢速批量插入
【发布时间】:2010-10-19 13:25:07
【问题描述】:

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

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

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

(我怀疑是索引很多,如果你也这么想,那我怎么能在操作前自动删除索引,然后再创建相同的索引呢?)

额外信息:

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

状态更新:

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

【问题讨论】:

    标签: sql sql-server indexing bulkinsert


    【解决方案1】:

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

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

    【讨论】:

    • 可能不想禁用 PK 索引(如果是聚集的话,绝对不会)。
    • @Lucero:感谢您的回答。一个建议:也许根据理查德的评论更新你的答案?
    【解决方案2】:

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

    重建索引时,首先构建聚集索引,反之,最后删除它。它们都应该具有 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
    

    【讨论】:

      【解决方案3】:

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

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

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

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

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

      【讨论】:

      • 感谢您的回答和其他问题。一次调用存储过程即可进行批量插入。
      • 我认为只使用 href="#751062" 链接到另一个答案可以避免重新加载页面。
      • @Ole:感谢关于链接的想法(回想起来很明显:-))。
      • 在事务中包装每个插入语句比批量插入要慢得多。
      【解决方案4】:

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

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

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

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

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

      【讨论】:

        猜你喜欢
        • 2011-08-07
        • 1970-01-01
        • 1970-01-01
        • 2020-05-14
        • 1970-01-01
        • 2018-08-27
        • 2013-10-14
        • 1970-01-01
        • 2020-06-26
        相关资源
        最近更新 更多