需要有关在SQL Server上使用SqlBulkCopy推送信封的建议

时间:2012-06-23 00:39:19

标签: sql-server scalability bulkinsert sqlbulkcopy database-performance

我正在设计一个应用程序,其中一个方面是它应该能够将大量数据接收到SQL数据库中。我将数据库stricture设计为具有bigint标识的单个表,如下所示:

CREATE TABLE MainTable
(
   _id bigint IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
    field1, field2, ...
)

我将省略我打算如何执行查询,因为它与我的问题无关。

我编写了一个原型,它使用SqlBulkCopy将数据插入到该表中。它似乎在实验室中运作良好。我能够以~3K记录/秒的速率插入数千万条记录(完整记录本身相当大,~4K)。由于此表上唯一的索引是自动递增bigint,因此即使推送了大量行,我也没有看到减速。

考虑到实验室SQL服务器是配置相对较弱的虚拟机(4Gb RAM,与其他VM磁盘sybsystem共享),我期望在物理机上获得明显更好的吞吐量,但它没有发生,或者说让性能提升可以忽略不计。我可以,也许在物理机器上插入速度提高25%。即使我配置了3驱动器RAID0,其执行速度比单个驱动器快3倍(由基准测试软件测量),但我没有任何改进。基本上:更快的驱动子系统,专用的物理CPU和双RAM几乎没有转化为任何性能增益。

然后我使用Azure上的最大实例(8核,16Gb)重复测试,我得到了相同的结果。因此,添加更多内核并未改变插入速度。

此时我已经使用了以下软件参数而没有任何显着的性能提升:

  • 修改SqlBulkInsert.BatchSize参数
  • 同时从多个线程插入,并调整线程数
  • 在SqlBulkInsert上使用表锁选项
  • 通过使用共享内存驱动程序从本地进程插入来消除网络延迟

我试图将性能提升至少2-3次,而我最初的想法就是投入更多的硬件才能完成,但到目前为止还没有。

所以,有人可以推荐我:

  • 什么资源可能被怀疑是瓶颈?怎么确认?
  • 考虑到有一个SQL服务器系统,是否有一种方法可以尝试获得可靠的可扩展批量插入改进?

更新我确定加载应用不是问题。它在一个单独的线程中创建一个临时队列中的记录,所以当有一个插入时它就像这样(简化):

===>start logging time
int batchCount = (queue.Count - 1) / targetBatchSize + 1;
Enumerable.Range(0, batchCount).AsParallel().
    WithDegreeOfParallelism(MAX_DEGREE_OF_PARALLELISM).ForAll(i =>
{
    var batch = queue.Skip(i * targetBatchSize).Take(targetBatchSize);
    var data = MYRECORDTYPE.MakeDataTable(batch);
    var bcp = GetBulkCopy();
    bcp.WriteToServer(data);
});
====> end loging time

记录时间,创建队列的部分永远不会占用任何重要的块

UPDATE2 我已经实现了收集该周期中每个操作所需的时间,布局如下:

  • queue.Skip().Take() - 可忽略不计
  • MakeDataTable(batch) - 10%
  • GetBulkCopy() - 可忽略不计
  • WriteToServer(data) - 90%

UPDATE3 我正在为标准版本的SQL设计,所以我不能依赖分区,因为它只在企业版中可用。但我尝试了一种分区方案的变体:

  • 创建了16个文件组(G0到G15),
  • 制作16个仅用于插入的表(T0到T15),每个表都绑定到其各自的组。表根本没有索引,甚至没有聚集的int identity。
  • 插入数据的线程将循环遍历所有16个表。这几乎可以保证每个批量插入操作都使用自己的表

这确实使散装插入物的产量提高了约20%。 CPU内核,LAN接口,驱动器I / O没有最大化,并且使用的最大容量约为25%。

UPDATE4 我认为它现在已经很好了。我能够使用以下技术将插入物推到合理的速度:

  • 每个批量插入都会进入自己的表格,然后将结果合并到主要的
  • 为每个批量插入重新创建表,使用表锁
  • 使用IDataReader实现from here而不是DataTable。
  • 从多个客户端完成的批量插入
  • 每个客户端使用单个千兆位VLAN
  • 访问SQL
  • 访问主表的边进程使用NOLOCK选项
  • 我检查了sys.dm_os_wait_stats和sys.dm_os_latch_stats以消除争用

我现在很难决定谁会因为回答的问题而获得赞誉。那些没有得到回答的人,我道歉,这是一个非常艰难的决定,我感谢你们所有人。

UPDATE5 :以下项目可以使用一些优化:

  • 使用IDataReader实现from here而不是DataTable。

除非您在具有大量CPU核心数的计算机上运行程序,否则它可能会使用一些重新分解。由于它使用反射生成get / set方法,因此成为CPU的主要负载。如果性能是关键,那么当您手动编写IDataReader时,它会增加很多性能,因此它会被编译,而不是使用反射

3 个答案:

答案 0 :(得分:4)

有关针对批量加载调优SQL Server的建议,请参阅MS中的Data Loading and Performance Guide论文,以及联机丛书中的Guidelines for Optimising Bulk Import。虽然它们专注于从SQL Server批量加载,但大多数建议都适用于使用客户端API进行批量加载。本文适用于SQL 2008 - 您没有说明您要针对哪个SQL Server版本 两者都有相当多的信息,值得详细介绍。但是,有些亮点:

  • 最小化记录批量操作。使用批量记录或简单恢复。 您可能需要启用traceflag 610(但请参阅有关做法的警告 这个)
  • 调整批量大小
  • 考虑对目标表进行分区
  • 在批量加载期间考虑删除索引

Data Loading and Performance Guide的流程图中很好地总结了: enter image description here

正如其他人所说,你需要得到一些性能计数器来确定瓶颈的来源,因为你的实验表明IO可能不是限制。 Data Loading and Performance Guide包括要监视的SQL等待类型和性能计数器的列表(文档中没有要链接的锚点,但在“优化批量加载”一节中,这大约是文档的75%)

更新

我花了一段时间才找到这个链接,但是Thomas Kejser的this SQLBits talk也非常值得一看 - 如果你没有时间观看整个事情,slides可用。它重复了这里链接的一些材料,但也涵盖了如何处理特定性能计数器的高发生率的其他一些建议。

答案 1 :(得分:2)

看来你已经做了很多但是我不确定你是否有机会学习Alberto Ferrari SqlBulkCopy Performance Analysis报告,该报告描述了考虑与SqlBulkCopy相关的性能的几个因素。我会说在那篇论文中讨论的很多东西仍然值得尝试,首先尝试。

答案 2 :(得分:1)

我不确定为什么你没有100%利用CPU,IO或内存。但是,如果您只是想提高批量加载速度,请考虑以下事项:

  1. 将数据文件分区为不同的文件。或者,如果它们来自不同的来源,则只需创建不同的数据文件。
  2. 然后同时运行多个批量插入。
  3. 根据您的情况,上述情况可能不可行;但如果可以,那么我相信它可以提高你的加载速度。