从C#并行化SQL Server中的大量插入(以获得更好的时间性能)

Meh*_*ANI 6 c# sql-server parallel-processing multithreading

问题陈述:如何在SQL Server中并行化插入(2008)

我正在为C#多线程工作者进行大规模的数值计算,基本上做一件事:在一段时间内(以天为单位)测试数千种可能的配置(矩阵组合)并将结果存储到SQL Server数据库中.

如果我将结果逐个存储到DB中(每个计算会话约300,000行*100个会话),一个接一个地,我最后等待数小时才能结束存储过程.

数据库设计非常简单:

  • 组合设置
    CS_ID1,值A1,值B1,值C1
    CS_ID2,值A2,值B2,值C2
    .........

  • 每日
    结果
    CS_ID1,第1 ,结果1 CS_ID1,第2
    天,结果2 CS_ID1,第3天,结果3
    .........

    .........
    CS_ID2,第1天,结果N
    CS_ID2,第2天,结果N + 1
    CS_ID2,第3天,结果N + 2

每个"组合集"都针对样本日进行测试,其每日结果在单个C#线程中处理,其中生成LINQ/SQL查询并在线程结束之前将其发送到DB.除组合集ID序列外,结果之间没有逻辑关系.这非常重要:这就是为什么我想要并行化插入内容,因为它基本上等于结果块的批量转储

另一个可能重要的细节是可以预先确定将多少行插入到数据库中(每块和总数).这可能有助于组织表空间,通过页面拆分它们,预先修复id范围以便同时存储块,或类似的东西(不,我不是"高"或者什么:-))

我欢迎任何建议,以使插入时间尽可能短.

请考虑到我是一名C#开发人员,具有非常基本的SQL Server知识,并且不熟悉深层技术DBA概念(我看到锁定调整非常多,也有多线程和异步功能,但我必须承认我独自迷失在森林里:-))

我有12个CPU核心可用,24Go RAM


编辑: 决胜局
我欢迎任何关于监控整个过程时间的聪明建议:从C#线程开始/结束到详细的SQl服务器插入报告(什么时候,如何,以及在哪里发生).
我尝试使用NLog记录,但它大大缩短了处理时间,因此我正在寻找一些非常无缝且效果最小的智能解决方法.对于SQL服务器部分也是如此:我知道有几个日志和监控SP可用.我还没弄清楚哪些适合我的情况.

Rem*_*anu 9

300k插入只需几秒钟,最糟糕的是几分钟,而不是几小时.你一定做错了.早在2008年的ETL SSIS世界纪录是2.36 TB /小时,30万条记录什么都没有.

基本的经验法则是:

  • 批量提交.这是最重要的事情.不要INSERT一行,然后INSERT一行,然后在nauseam INSERT一行,每个插入int 自己的事务.你的程序必须等待日志(LDF)在每个语句之后刷新,并且速度很慢.非常慢.而是启动一个事务,然后插入一批行,然后提交事务:

伪代码:

do
  {
  using (TransactionScope scope = new TransactionScope(
     Required, new TransactionOptions() {IsolationLevel = ReadCommitted))
  {
    for (batchsize)
    {
      ExecuteNonQuery ("Insert ...")
    }
    scope.Complete ();
  }
} while (!finished);
Run Code Online (Sandbox Code Playgroud)

单独的第一个选项将使您每秒超过3000次插入(300k时约为2分钟).第二个选项可以让你达到每秒数万个范围.如果您需要更多,还有更高级的技巧:

  • 使用堆而不是b-trees(没有聚簇索引)
  • 禁用二级索引
  • 将客户端关联到软NUMA节点并按每个客户端连接进入锁定表,然后在最后使用分区切换将它们全部切换.这是真正的高端,每秒数百万行.

我建议你从基础知识的基础知识开始:批量提交.


Wil*_*ler 5

BULK INSERT可能有帮助.


thk*_*ala 5

如果您为每个插入使用单独的事务,那肯定会影响性能,因为DB服务器必须以原子方式执行每个插入.我从来没有使用过SQL服务器,但大多数SQL变种都有办法在一个事务中捆绑多个插入,通常有类似的东西

BEGIN TRANSACTION;

...<various SQL statements>...

COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)

有关SQL Server语法,请参阅:

http://msdn.microsoft.com/en-us/library/ms188929.aspx

http://msdn.microsoft.com/en-us/library/ms190295.aspx

根据我的经验,这样的捆绑插件肯定有助于提高服务器性能,并在某种程度上有助于资源和网络的使用.

编辑:

大多数(所有?)不错的数据库服务器使用某种每行锁定,而不是每个表锁.您应该能够拥有多个并发事务,每个事务都有多个插入,没有问题 - 这就是数据库服务器的设计目标.您当然可以让每个工作线程执行自己的事务,从而并行化来自不同线程的插入.

由于您显然使用单台计算机进行计算和数据库,因此大量并行化数据库事务不会对性能造成太大影响,甚至可能使情况变得更糟,因为您实际上没有任何网络延迟来减少影响.只要所有CPU核心都忙,这可能意味着许多工作人员> = 12,您应该考虑其他优化.

如果您的线程处理之后一次性生成它们的输出(例如,如果您计算一个大矩阵然后转储到数据库中)我怀疑您通过将结果存储到文件然后让DB将其读回到一个文件中而获得任何收益表.

另一方面,如果您的线程逐个进行输出,则可以将其输出的部分存储在内存中,然后将这些部分插入数据库,每轮执行多个事务.如果 CPU未充分利用,在这种情况下增加工作线程数可能允​​许您在DB存储数据时具有更好的CPU利用率.

将工作者输出存储在文件中应该避免恕我直言,因为它有效地使磁盘子系统上的负载增加了三倍.您可能想要这样做的唯一原因是,如果您确实没有用于中间存储结果的内存.