簡體   English   中英

加速LINQ插入

[英]Speed up LINQ inserts

我有一個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行,ADO.net花了大約20分鍾。 LINQ在25分鍾內增加了大約30,000的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目前每isntance,你也可能會浪費大量的時間檢查三角洲-毫無意義的,如果你只改變了一行。 只批量調用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<DataContext, int> 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);

如果你想優化它,我想你會走上正軌。 也許使用秒表課做一些有助於證明我對錯的時機。

通常,當不使用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();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM