加速LINQ插入

我有一个CSV文件,我必须将其插入SQL Server数据库。 有没有办法加快LINQ插入?

我创建了一个简单的Repository方法来保存记录:

public void SaveOffer(Offer offer) { Offer dbOffer = this.db.Offers.SingleOrDefault ( o => o.offer_id == offer.offer_id); // add new offer if (dbOffer == null) { this.db.Offers.InsertOnSubmit(offer); } //update existing offer else { dbOffer = offer; } this.db.SubmitChanges(); } 

但是使用这种方法,程序比使用ADO.net SQL插入插入数据慢得多(新的SqlConnection,新的SqlCommand用于选择,如果存在,新的SqlCommand用于更新/插入)。

在100k csv行上,ADO.net方式需要大约一个小时,大约需要1分钟左右。 对于2M csv行,它花了大约20分钟ADO.net。 LINQ在25分钟内增加了大约30k的2M行。 我的数据库有3个表,在dbml中链接,但其他两个表都是空的。 测试是在所有表空的情况下进行的。

PS我曾尝试使用SqlBulkCopy,但我需要在将其插入数据库之前对Offer进行一些转换,我认为这违背了SqlBulkCopy的目的。

更新/编辑:18小时后,LINQ版本增加了大约200K行。

我也使用LINQ插件测试了导入,与ADO.net相比也非常慢。 我没有看到插入/提交更改和选择/更新/插入/提交更改之间的重大区别。

我仍然需要尝试批量提交,手动连接到db和编译的查询。

SubmitChanges不会批量更改,它会为每个对象执行一次插入语句。 如果你想快速插入,我认为你需要停止使用LINQ。

在SubmitChanges正在执行时,启动SQL事件探查器并观察正在执行的SQL。

请参阅问题“LINQ to SQL可以执行批量更新和删除吗?或者它是否总是一次更新一行?” 这里: http : //www.hookedonlinq.com/LINQToSQLFAQ.ashx

它链接到这篇文章: http : //www.aneyfamily.com/terryandann/post/2008/04/Batch-Updates-and-Deletes-with-LINQ-to-SQL.aspx ,它使用扩展方法来修复linq的无法使用批量插入和更新等

您是否尝试在事务中包装插入和/或延迟db.SubmitChanges以便批量插入几个?

事务通过减少fsync()的需求来帮助吞吐量,并且延迟db.SubmitChanges将减少.NET < - > db往返的数量。

编辑:有关更多优化,请参阅http://www.sidarok.com/web/blog/content/2008/05/02/10-tips-to-improve-your-linq-to-sql-application-performance.html原则。

查看下面的页面,了解如何更改代码以使用批量插入而不是使用LINQ的InsertOnSubmit()函数。

您只需要将(提供的) BulkInsert类添加到代码中,对代码进行一些细微的更改,您就会看到性能的巨大提升。

Mikes知识库 – 使用LINQ进行BulkInserts

祝好运 !

我想知道你是否在数据上下文中累积了过多的数据集,这使得在内部身份缓存(在SingleOrDefault期间检查一次,以及我希望“未命中”)中解析行的速度很慢在实体实现时看到第二次打击)。

我不记得100%的短路是否适用于SingleOrDefault (尽管它将在.NET 4.0中使用 )。

我会尝试放弃数据上下文(提交更改并替换为空的)每n次操作一些n – 可能250或者其他。


鉴于你现在正在调用SubmitChanges ,你也可能浪费大量时间检查增量 – 如果你只改变了一行则没有意义。 只批量调用SubmitChanges ; 不是每个记录。

亚历克斯给出了最好的答案,但我认为有些事情正在被忽视。

您在这里遇到的主要瓶颈之一是分别为每个项目调用SubmitChanges。 我认为大多数人都不知道的一个问题是,如果您没有自己手动打开DataContext的连接,那么DataContext将自己重复打开和关闭它。 但是,如果您自己打开它,然后在完成后自行关闭它,事情会运行得更快,因为它不必每次都重新连接到数据库。 当我试图找出为什么DataContext.ExecuteCommand()在一次执行多个命令时如此令人难以置信地缓慢时,我发现了这一点。

还有一些其他方面可以加快速度:

虽然Linq To SQL不支持您的直接批处理,但您应该等到调用SubmitChanges(),直到您首先分析了所有内容。 每次InsertOnSubmit调用后,您都不需要调用SubmitChanges()。

如果实时数据完整性不是非常重要,您可以在开始检查商品是否已存在之前从服务器检索offer_id列表。 这可以显着减少您调用服务器以获取现有项目的次数,即使它不存在也是如此。

为什么不将offer []传递给该方法,并在将缓存提交到数据库之前对缓存进行所有更改。 或者您可以使用组进行提交,因此您不会用完缓存。 最重要的是你发送数据需要多长时间,浪费的最大时间是关闭和打开连接。

将此转换为已编译的查询是我能想到的最简单的方法来提高您的性能:

更改以下内容:

  Offer dbOffer = this.db.Offers.SingleOrDefault ( o => o.offer_id == offer.offer_id); 

至:

 Offer dbOffer = RetrieveOffer(offer.offer_id); private static readonly Func RetrieveOffer { CompiledQuery.Compile((DataContext context, int offerId) => context.Offers.SingleOrDefault(o => o.offer_id == offerid)) } 

仅此更改不会使其与ado.net版本一样快,但它将是一项重大改进,因为如果没有编译的查询,则每次运行此方法时都会动态构建表达式树。

正如已经提到过的一张海报,您必须重构代码,以便在您希望获得最佳性能时仅调用一次提交更改。

在将记录插入数据库之前,您是否真的需要检查记录是否存在。 我认为它看起来很奇怪,因为数据来自csv文件。

PS我曾尝试使用SqlBulkCopy,但我需要在将其插入数据库之前对Offer进行一些转换,我认为这违背了SqlBulkCopy的目的。

我认为它根本没有打败目的,为什么会这样呢? 只需用csv中的所有数据填充一个简单的数据集,然后执行SqlBulkCopy。 我做了类似的事情,收集了30000多行,导入时间从几分钟到几秒钟

我怀疑这不是插入或更新操作需要很长时间,而是确定您的商品是否已经存在的代码:

 Offer dbOffer = this.db.Offers.SingleOrDefault ( o => o.offer_id == offer.offer_id); 

如果你想优化这一点,我想你会走在正确的轨道上。 也许使用秒表课做一些有助于certificate我对错的时机。

通常,当不使用Linq-to-Sql时,您将拥有一个插入/更新过程或sql脚本,用于确定您传递的记录是否已存在。 你在Linq做了这个昂贵的操作,肯定永远不会希望匹配本机sql的速度(当你使用SqlCommand时会发生这种情况并选择是否存在记录)查找主键。

那么你必须要了解linq为你所做的所有ADO操作动态创建代码而不是手写,所以它总是比你的手动代码花费更多的时间。 它只是编写代码的简单方法,但如果你想谈论性能,ADO.NET代码总是会更快,这取决于你如何编写代码。

我不知道linq是否会尝试重复使用它的最后一个语句,如果确实如此,那么使用更新批处理分离插入批处理可能会略微提高性能。

此代码运行正常,并阻止大量数据:

 if (repository2.GeoItems.GetChangeSet().Inserts.Count > 1000) { repository2.GeoItems.SubmitChanges(); } 

然后,在批量插入结束时,使用:

 repository2.GeoItems.SubmitChanges();