问题陈述:如何在SQL Server(2008)中并行插入
我正在为C#多线程工作人员进行大规模的数值计算,基本上只做一件事:在一段时间内(以天为单位)测试数千种可能的配置(矩阵组合)并将结果存储到SQL Server数据库中。
如果我将结果逐个存储到DB中(每个计算会话约300,000行* 100个会话),一个接一个,我最后等待数小时才能结束存储过程。 BR />
数据库设计非常简单:
每个“组合集”都针对样本日进行测试,其每日结果在单个C#线程中处理,其中生成LINQ / SQL查询并在线程结束之前将其发送到DB。除组合集ID序列外,结果之间没有逻辑关系。这非常重要:这就是为什么我想将插入内容并行化为 它基本上等于结果块的批量转储
另一个可能很重要的细节是 可以预先确定将多少行插入数据库 (每个块和总)。这可能有助于组织表空间,通过页面拆分它们,预先修复id范围以便同时存储块,或类似的东西(不,我不是“高”或者什么:-))< / p>
我欢迎任何建议,以使插入时间尽可能短
请考虑到我是一名C#开发人员,具有非常基本的SQL Server知识,并且不熟悉深层技术DBA概念(我看到锁定调整非常多,也有多线程和异步功能,但我不得不承认我独自迷失在森林里:-))
我有12个CPU核心可用,24Go RAM
编辑:
决胜局
我欢迎任何关于监控整个过程时间的聪明建议:从C#thread inception / end到详细的SQl服务器插入报告(会发生什么时候,如何以及在哪里)。
我尝试使用NLog记录,但它大大缩短了处理时间,因此我正在寻找一些非常无缝且效果最小的智能解决方法。对于SQL服务器部分也是如此:我知道有几个日志和监控SP可用。我还没弄清楚哪些适合我的情况。
答案 0 :(得分:8)
300k插入只需几秒钟,最糟糕的是几分钟,而不是几小时。你一定做错了。 2008年的ETL SSIS world record时速为2.36 TB /小时,30万条记录没有。
基本的经验法则是:
伪代码:
do
{
using (TransactionScope scope = new TransactionScope(
Required, new TransactionOptions() {IsolationLevel = ReadCommitted))
{
for (batchsize)
{
ExecuteNonQuery ("Insert ...")
}
scope.Complete ();
}
} while (!finished);
SqlBulkCopy
单独的第一个选项将使您每秒超过3000次插入(300k时约为2分钟)。第二个选项可以让你达到每秒数万个范围。如果您需要更多,还有更高级的技巧:
我建议你从基础知识的基础知识开始:批量提交。
答案 1 :(得分:5)
BULK INSERT
可能对此有所帮助。
答案 2 :(得分:5)
如果您为每个插入使用单独的事务,那肯定会影响性能,因为DB服务器必须以原子方式执行每个插入。我从来没有使用过SQL服务器,但是大多数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
根据我的经验,捆绑这样的插件肯定有助于提高服务器性能,并在某种程度上有助于资源和网络的使用。
编辑:
大多数(全部?)不错的数据库服务器使用某种每行锁定,而不是每个表锁。您应该能够拥有多个并发事务,每个事务都有多个插入,没有问题 - 这就是数据库服务器的设计目标。您当然可以让每个工作线程执行自己的事务,从而并行化来自不同线程的插入。
由于您显然使用单台计算机进行计算和数据库,因此广泛并行化数据库事务不会对性能造成太大影响,甚至可能使情况变得更糟,因为您实际上没有任何网络延迟来减少影响的。只要所有CPU核心都忙,这可能意味着许多工作人员&gt; = 12,你应该考虑其他优化。
如果您的线程在处理之后一次性生成输出(例如,如果您计算一个大矩阵并且然后转储到数据库中)我怀疑您会获得任何东西通过将结果存储到文件中,然后让DB将其读回表中。
另一方面,如果您的线程逐个输出,您可能会将其输出的部分存储在内存中,然后将这些部分插入数据库,每轮执行多个事务。在这种情况下,增加工作线程的数量可能允许您在DB存储数据时具有更好的CPU利用率,如果 CPU未充分利用。
应该避免将工作者输出存储在文件中,因为它有效地使磁盘子系统上的负载增加了三倍。您可能想要这样做的唯一原因是,如果您确实没有用于中间存储结果的内存。
答案 3 :(得分:2)
这是一篇关于使用C#进行批量插入的文章: http://blogs.msdn.com/b/nikhilsi/archive/2008/06/11/bulk-insert-into-sql-from-c-app.aspx
有关使用C#批量插入的其他想法是在Stack Overflow问题中: What’s the best way to bulk database inserts from c#?
希望这有帮助。
答案 4 :(得分:1)
答案 5 :(得分:1)
您可以尝试使用Parallel For进行插入...
...但我会首先尝试BULK INSERT或Batch commit ...
答案 6 :(得分:1)
这是一个有趣的问题。首先,您如何使用数据库中的值?他们参与后续计算还是数据库只是“转储”以存储结果以供以后处理?您也是一天24小时运行的申请/流程?
我为什么要问 - 如果你可以拆分“存储结果”和“处理结果”操作,你可以通过从一个会话中“填充”数据并将它们存储为一个blob来实现更高的吞吐量。之后,在最近的时间,您可以走路并处理并将这些blob“扩展”为表格,例如使用作业或其他流程。从理论上讲,如果这样可以,那么您可以将这些“暂存”blob存储在二进制文件中,而不是直接存储在数据库中,以实现最大可能的写入速度(仅受文件系统,操作系统和底层磁盘硬件的限制)。 p>