代码之家  ›  专栏  ›  技术社区  ›  Mehdi LAMRANI

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

  •  6
  • Mehdi LAMRANI  · 技术社区  · 14 年前

    问题语句:如何在SQL Server中并行插入(2008)

    我在C多线程工作人员中为科学研究执行大量的数值计算,基本上只做一件事:在一段时间内(以天为单位)测试数千种可能的配置(矩阵组合),并将结果存储到SQL Server数据库中。

    如果我一个接一个地将结果存储到数据库中(每个计算会话大约300.000行*100个会话),我最终会等待数小时,等待存储过程结束。

    数据库设计非常简单:

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

    • 每天的结果
      CS U ID1,第1天,结果1
      CS-ID1,第2天,结果2
      CS-ID1,第3天,结果3
      .........

      。。。。。。。。。
      CS-ID2,第1天,结果N
      第2天,结果N+1
      CS U ID2,第3天,结果N+2

    每一个“组合集”都是根据样本日进行测试的,其每天的结果在一个C#线程中处理,在线程结束前生成一个LINQ/SQL查询并发送给DB。除了组合集合id序列, 结果之间没有逻辑关系 . 这一点非常重要:这就是为什么我把insert的内容并行化为 它基本上相当于结果块的大容量转储

    另一个重要的细节是 可以预先确定将向数据库中插入多少行 (每个区块和总计)。这可能有助于组织表空间,将它们拆分成页面,预先修复id范围以便同时存储块,或者类似的东西(不,我不是“high”或其他东西:-)

    为了尽可能缩短插入时间,我欢迎任何建议。

    请考虑到我是一个C#开发人员,拥有非常基本的SQL Server知识,不太熟悉深度的DBA技术概念(我看到锁的调整非常多,还有多线程和异步功能,但我不得不承认我在森林中迷失了自我:-)

    我有12个可用的CPU内核和24个RAM


    编辑: 断线器
    我欢迎任何有关监视整个过程时间的聪明建议:从C#线程起始/结束到详细的SQl server插入报告(何时、如何和何地发生的情况)。
    我试着用NLog登录,但它严重地偏离了处理时间,所以我正在寻找一些智能的解决方案,非常无缝,影响最小。对于SQL server部分也一样:我知道有一些日志和监视SP可用。我还不知道哪一个适合我的情况。

    7 回复  |  直到 14 年前
        1
  •  5
  •   thkala jaxb    14 年前

    如果为每个插入使用单独的事务,这肯定会影响性能,因为DB服务器必须原子地执行每个插入。我从未使用过SQL server,但是大多数SQL变体都有一种方法可以在一个事务中组合多个插入,通常是

    BEGIN TRANSACTION;
    
    ...<various SQL statements>...
    
    COMMIT TRANSACTION;
    

    有关SQL server语法,请参见:

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

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

    以我的经验,像这样捆绑插入肯定有助于提高服务器性能,在某种程度上,也有助于资源和网络的使用。

    编辑:

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

    由于您显然是使用一台计算机进行计算和数据库,因此广泛并行化数据库事务不会对性能产生太大影响,甚至可能会使其更糟,因为您实际上没有任何网络延迟来减少的影响。只要所有的CPU内核都很忙(这可能意味着有许多工作线程>=12),就应该考虑其他优化。

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

    另一方面,如果您的线程一个接一个地执行其输出,您可能会受益于将其输出的一部分存储在内存中,然后将这些部分插入到数据库中,每轮执行多个事务。在这种情况下,增加工作线程的数量可以让您在数据库存储数据时有更好的CPU利用率, 如果 CPU未充分利用。

    应该避免将工作机输出存储在文件中,因为它有效地将磁盘子系统上的负载增加了三倍。您可能想要这样做的唯一原因是,如果您真的没有内存来中间存储结果。

        2
  •  9
  •   Remus Rusanu    14 年前

    300k插入数是几秒钟的事,最坏的是几分钟,而不是几小时。你一定做错了。这个 ETL SSIS world record 2008年是每小时2.36 TB,有30万的记录 没有什么 .

    基本经验法则是:

    • 批量提交 . 这是最重要的。不插入行,然后插入行,然后在nauseam插入行,每个插入int 自己的交易 . 您的程序必须等待日志(LDF)在每个语句结束后刷新它的案例,并且会很慢。非常慢。而是启动事务,然后插入一批行,然后提交事务:

    伪码:

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

    仅第一个选项就可以使您每秒插入3000个以上(300k大约2分钟)。第二个选项会让你进入每秒数万次的射程。如果你需要更多,这里有更高级的技巧:

    • 使用堆而不是b树(没有聚集索引)
    • 禁用辅助索引
    • 将客户机与软NUMA节点关联,并按客户机连接推入锁定的表,然后在最后使用分区切换将它们全部切换。这是给 真的? 高端,每秒数百万行。

    我建议您从基础知识开始:批量提交。

        3
  •  5
  •   Will Marcouiller    14 年前

    这个 BULK INSERT 可能会有帮助。

        4
  •  2
  •   Community CDub    7 年前

    下面是一篇关于使用C#进行批量插入的文章: http://blogs.msdn.com/b/nikhilsi/archive/2008/06/11/bulk-insert-into-sql-from-c-app.aspx

    关于使用C#进行大容量插入的其他想法是堆栈溢出问题: What’s the best way to bulk database inserts from c#?

    希望这有帮助。

        5
  •  1
  •   Gerardo Grignoli    14 年前

    你可以尝试使用 Parallel For 要插入。。。

    ... 但我会先尝试批量插入或批量提交。。。

        6
  •  1
  •   Pavel Urbančík    14 年前

    这是个有趣的问题。首先,如何使用数据库中的值?它们是否参与后续计算,或者数据库只是“转储”以存储结果以供以后处理?您的应用程序/进程是否每天24小时运行?
    为什么我要问-如果您可以将“存储结果”和“处理结果”操作分开,您可以通过“blobing”一个会话中的数据并将它们存储为一个blob来获得更高的吞吐量。稍后,在离开peek时,您可以遍历和处理这些blob,并将其“扩展”到表中,例如使用job或其他进程。理论上,如果这是可以的,你可以将这些“分级”块存储在二进制文件中,而不是直接存储在数据库中,以实现可能的最大可能的写入速度(仅限于文件系统、OS和底层磁盘硬件)。

        7
  •  1
  •   John Raymund    13 年前

    也许这对你有帮助

    我有一个关于如何在SQL中执行并行存储过程的逐步指南 here .

    您可以将大容量插入与此插入合并。