在SQL Server(C#客户端)中批量插入大量数据的最快方法是什么

在SQL Server(C#客户端)中批量插入大量数据的最快方法是什么

What's the fastest way to bulk insert a lot of data in SQL Server (C# client)

我的C#客户端将大量数据插入SQL Server 2005数据库时遇到了一些性能瓶颈,我正在寻找加快该过程的方法。

我已经在使用SqlClient.SqlBulkCopy(基于TDS)来加快通过电线的数据传输,这很有帮助,但是我仍在寻找更多。

我有一个简单的表,看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 CREATE TABLE [BulkData](
 [ContainerId] [int] NOT NULL,
 [BinId] [smallint] NOT NULL,
 [Sequence] [smallint] NOT NULL,
 [ItemId] [int] NOT NULL,
 [Left] [smallint] NOT NULL,
 [Top] [smallint] NOT NULL,
 [Right] [smallint] NOT NULL,
 [Bottom] [smallint] NOT NULL,
 CONSTRAINT [PKBulkData] PRIMARY KEY CLUSTERED
 (
  [ContainerIdId] ASC,
  [BinId] ASC,
  [Sequence] ASC
))

我正在将数据插入平均约300行的数据块中,其中每个数据块中的ContainerId和BinId都是恒定的,并且Sequence值为0-n,并且这些值基于主键进行了预排序。

%Disk时间性能计数器在100%上花费大量时间,因此很明显磁盘IO是主要问题,但是我获得的速度比原始文件副本低几个数量级。

如果我有帮助,是否有帮助:

  • 插入时放下主键,稍后再创建
  • 将插入到具有相同架构的临时表中,并定期将其转移到主表中,以保持发生插入的表的大小较小
  • 还要别的吗?
  • -
    根据我得到的答复,让我澄清一下:

    Portman:我使用的是聚集索引,因为当数据全部导入后,我将需要按该顺序顺序访问数据。导入数据时,我特别不需要索引在那儿。与完全删除导入约束相比,在执行插入操作时具有非聚集的PK索引有什么好处吗?

    Chopeen:数据正在许多其他机器上远程生成(我的SQL Server当前只能处理大约10个,但是我希望能够添加更多)。在本地计算机上运行整个过程是不切实际的,因为这样一来,它必须处理50倍的输入数据才能生成输出。

    Jason:在导入过程中,我没有对该表进行任何并发查询,我将尝试删除主键,看看是否有帮助。


    在此处,您可以在SQL Server中禁用/启用索引:

    1
    2
    3
    --Disable Index ALTER INDEX [IX_Users_UserID] SalesDB.Users DISABLE
    GO
    --Enable Index ALTER INDEX [IX_Users_UserID] SalesDB.Users REBUILD

    以下一些资源可帮助您找到解决方案:

    一些批量装载速度的比较

    使用SqlBulkCopy将数据从客户端快速加载到SQL Server

    优化大容量复印性能

    绝对要查看NOCHECK和TABLOCK选项:

    表提示(Transact-SQL)

    插入(Transact-SQL)


    您已经在使用SqlBulkCopy,这是一个很好的开始。

    但是,仅使用SqlBulkCopy类并不一定意味着SQL将执行批量复制。特别是,SQL Server执行高效的大容量插入必须满足一些要求。

    进一步阅读:

    • 批量导入时最少记录的前提条件
    • 优化批量导入性能

    出于好奇,为什么要这样设置索引?看来ContainerId / BinId / Sequence更适合作为非聚集索引。您是否有特定的原因想要将该索引进行聚簇?


    我的猜测是,如果将该索引更改为非聚集索引,将会看到巨大的进步。这为您提供了两个选择:

  • 将索引更改为非聚集索引,并将其保留为堆表,而不包含聚集索引
  • 将索引更改为非聚集索引,然后添加一个代理键(例如" id ")并使其成为标识,主键和聚集索引
  • 任何一种都可以加快插入速度,而不会明显降低读取速度。

    以这种方式进行思考-现在,您正在告诉SQL进行批量插入,但是随后您要求SQL对添加了表的每个表重新排序整个表。使用非聚集索引,您可以按记录的顺序添加记录,然后构建一个单独的索引以指示所需的顺序。


    您是否尝试过使用交易?

    根据您的描述,让服务器将100%的时间提交到磁盘,似乎您正在使用原子SQL语句发送每一行数据,从而迫使服务器每行提交(写入磁盘)。铅>

    如果您改用事务,则服务器在事务结束时只会提交一次。

    以获得更多帮助:您正在使用哪种方法将数据插入服务器?使用DataAdapter更新DataTable,还是使用字符串执行每个句子?


    我并不是一个聪明的人,我对SqlClient.SqlBulkCopy方法没有太多的经验,但这是我的2美分。我希望它能对您和其他人有所帮助(或至少使人们喊出我的无知;)。

    除非您的数据库数据文件(mdf)与事务日志文件(ldf)位于不同的物理磁盘上,否则您将永远无法匹配原始文件的复制速度。此外,任何聚簇索引也需要位于单独的物理磁盘上,以进行更公平的比较。

    您的原始副本未记录日志或维护选择字段(列)的排序顺序以建立索引。

    我同意波特曼在创建非聚集身份种子并将您现有的非聚集索引更改为聚集索引的情况。

    关于您在客户端上使用的构造...(数据适配器,数据集,数据表等)。如果服务器上的磁盘io为100%,我认为您最好不要花时间分析客户端结构,因为它们似乎比服务器当前处理的速度快。

    如果您遵循Portman的有关最少日志记录的链接,那么我认为将大容量副本包含在事务中不会有什么帮助,但我一生中错了很多次;)

    这并不一定会立即对您有所帮助,但是如果您发现当前问题,则下一条注释可能会帮助解决下一个瓶颈(网络吞吐量),尤其是在Internet上的情况下。

    Chopeen也问了一个有趣的问题。您如何确定要使用300个记录计数块插入? SQL Server具有默认的数据包大小(我相信它是4096字节),对我来说,导出记录的大小并确保您有效利用了在客户端和服务器之间传输的数据包是很有意义的。 (请注意,您可以在客户端代码上更改数据包大小,而不是更改服务器选项,这显然会更改所有服务器通信的数据包-可能不是一个好主意。)例如,如果您的记录大小导致300个记录批次需要4500个记录,字节,您将发送2个数据包,而第二个数据包大部分被浪费了。如果批记录记录数是任意分配的,则可以进行一些简单的数学运算。

    据我所知(记住数据类型的大小),每个记录正好有20个字节(如果int = 4字节和smallint = 2字节)。如果您正在使用300个记录计数批处理,那么您将尝试发送300 x 20 = 6,000字节(另外,我猜测该连接会产生一些开销,等等)。您可能更有效地以200个记录计数批次发送这些消息(200 x 20 = 4,000个开销空间)= 1个数据包。再一次,您的瓶颈似乎仍然是服务器的磁盘io。

    我知道您正在使用相同的硬件/配置将原始数据传输与SqlBulkCopy进行比较,但是如果挑战是我的,这也是我要去的地方:

    这篇文章可能很老了,可能不再对您有帮助,但是我接下来要问一下您的磁盘的RAID配置是什么,以及您使用的磁盘速度是多少?尝试将日志文件放在使用RAID 10且数据文件上使用RAID 5(理想情况下为1)的驱动器上。这可以帮助减少大量的主轴移动到磁盘上的不同扇区,并导致更多的读取/写入时间,而不是非生产性的"移动"状态。如果您已经将数据和日志文件分开,则索引是否与数据文件位于不同的物理磁盘驱动器上(只能使用聚集索引来做到这一点)。这不仅允许通过数据插入并发地更新日志记录信息,还可以允许索引插入(以及任何代价高昂的索引页操作)同时发生。


    BCP-设置很麻烦,但是自DB诞生以来就已经存在,而且非常快。

    除非您按照该顺序插入数据,否则三部分索引确实会减慢速度。稍后应用它也会确实减慢速度,但是将是第二步。

    Sql中的复合键总是很慢,键越大,速度越慢。


    我认为听起来可以使用SSIS包来完成。它们类似于SQL 2000的DTS软件包。我已经使用它们成功地转换了纯文本CSV文件,现有SQL表,甚至包括跨多个工作表的6位行的XLS文件的所有内容。您可以使用C#将数据转换为可导入的格式(CSV,XLS等),然后让您的SQL Server运行计划的SSIS作业以导入数据。

    创建SSIS包非常容易,SQL Server的企业管理器工具内置了一个向导(我认为标记为"导入数据"),并且在向导末尾它使您可以选择将其另存为SSIS包。 Technet上还有很多信息。


    是的,您的想法会有所帮助。
    如果在加载过程中没有读取事件,请选择选项1。
    如果在处理过程中正在查询目标表,请使用选项2。

    @安德鲁
    问题。您要插入的块数为300。插入的总数量是多少? SQL Server应该能够非常快速地处理300个普通的旧插入。


    推荐阅读