2010-09-20 86 views
8

我使用LinqToSql從C#向SqlServer 2008 express數據庫插入大量記錄。它看起來像插入是非常緩慢的。以下是代碼片段。使用Linq to Sql非常緩慢的插入過程

public void InsertData(int id) 
{ 

    MyDataContext dc = new MyDataContext(); 

    List<Item> result = GetItems(id); 

    foreach (var item in result) 
    { 
    DbItem dbItem = new DbItem(){ItemNo = item.No, ItemName=item.Name}; 
    dc.Items.InsertOnSubmit(); 
    } 

    dc.SubmitChanges(); 
} 

我做錯了什麼?或者使用Linq插入大量記錄是不好的選擇?

更新:感謝所有的答案。 @ p.campbell:對不起,記錄計數,這是一個錯字,實際上是大約100000.記錄也範圍到200K以及。

根據所有的建議,我把這個操作分成幾部分(也是一個需求變更和設計決策),並以小塊的形式檢索數據,並在它們到達時將它們插入到數據庫中。我已經將這個InsertData()方法放在線程操作中,並且現在使用SmartThreadPool來創建25個線程池來執行相同的操作。在這種情況下,我一次只插入100條記錄。現在,當我使用Linq或sql查詢時,它在時間方面沒有任何區別。

根據我的要求,此操作計劃每小時運行一次,併爲大約4k-6k用戶提取記錄。因此,現在我將每個用戶數據(檢索並插入數據庫)作爲一項任務並分配給一個線程。現在整個過程大約需要45分鐘,大約有25萬條記錄。

有沒有更好的方法來完成這種任務?或者任何人都可以建議我如何改進這個過程?

+1

有多少條記錄,以及該操作需要多長時間?這裏使用了哪些數據類型? – 2010-09-20 11:43:52

+0

超過1000000條記錄,主要是字符串數據類型但不超過10個字段。 – JPReddy 2010-09-20 12:36:10

+1

無論如何,一百萬張插頁需要時間。我懷疑,如果您複製生成的SQL語句(全部100萬個語句),並且特別運行它們,您將看不到與Management Studio差別很大的SQL語句! – 2010-09-20 13:17:33

回答

11

對於在oner

LINQ的或SqlCommand時,neither are designed for bulk copying data into SQL插入數據的巨量到SQL。

您可以使用SqlBulkCopy class,它提供對bcp實用程序的託管訪問,以便從幾乎任何數據源向Sql批量加載數據。

SqlBulkCopy類可用於將數據只寫入SQL Server表。但是,數據源不限於SQL Server;任何數據源都可以使用,只要數據可以加載到DataTable實例或使用IDataReader實例讀取即可。

性能比較

SqlBulkCopy的是目前爲止最快的,從一個簡單的CSV文件中加載數據時也是如此。

Linq只會在SQL中生成一個Insert語句的負載並將它們發送到您的SQL Server。這與使用SqlCommand的臨時查詢沒有什麼不同。 SqlCommand與Linq的性能幾乎相同。

的證明

(SQL Express的2008,.NET 4.0)

SqlBulkCopy的

使用SqlBulkCopy的加載從CSV文件100000行(包括加載數據)

using (SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=EffectCatalogue;Data Source=.\\SQLEXPRESS;")) 
{ 
    conn.Open(); 
    Stopwatch watch = Stopwatch.StartNew(); 

    string csvConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\data\\;Extended Properties='text;'"; 
    OleDbDataAdapter oleda = new OleDbDataAdapter("SELECT * FROM [test.csv]", csvConnString); 
    DataTable dt = new DataTable(); 
    oleda.Fill(dt); 

    using (SqlBulkCopy copy = new SqlBulkCopy(conn)) 
    { 
     copy.ColumnMappings.Add(0, 1); 
     copy.ColumnMappings.Add(1, 2); 
     copy.DestinationTableName = "dbo.Users"; 
     copy.WriteToServer(dt); 
    } 
    Console.WriteLine("SqlBulkCopy: {0}", watch.Elapsed); 
} 

SqlCommand

using (SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=TestDb;Data Source=.\\SQLEXPRESS;")) 
{ 
    conn.Open(); 
    Stopwatch watch = Stopwatch.StartNew(); 
    SqlCommand comm = new SqlCommand("INSERT INTO Users (UserName, [Password]) VALUES ('Simon', 'Password')", conn); 
    for (int i = 0; i < 100000; i++) 
    { 
     comm.ExecuteNonQuery(); 
    } 
    Console.WriteLine("SqlCommand: {0}", watch.Elapsed); 
} 

LinqToSql

using (SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=TestDb;Data Source=.\\SQLEXPRESS;")) 
{ 
    conn.Open(); 
    Stopwatch watch = Stopwatch.StartNew(); 
    EffectCatalogueDataContext db = new EffectCatalogueDataContext(conn); 
    for (int i = 0; i < 100000; i++) 
    { 
     User u = new User(); 
     u.UserName = "Simon"; 
     u.Password = "Password"; 
     db.Users.InsertOnSubmit(u); 
    } 
    db.SubmitChanges(); 
    Console.WriteLine("Linq: {0}", watch.Elapsed); 
} 

結果

SqlBulkCopy: 00:00:02.90704339 
SqlCommand: 00:00:50.4230604 
Linq: 00:00:48.7702995 
+1

這個表現真的很棒。感謝那個建議,我現在正在使用它。 – JPReddy 2010-09-23 07:18:15

3

如果你插入大量的數據記錄,你可以嘗試BULK INSERT

據我所知,在Linq to SQL中沒有等價的批量插入。

3

你已經調用了SubmitChanges(),這很好。這意味着只有一個連接和事務正在使用。

請考慮重構代碼以使用InsertAllOnSubmit()代替。

List<dbItem> newItems = GetItems(id).Select(x=> new DbItem{ItemNo = x.No, 
                  ItemName=x.Name}) 
            .ToList(); 
db.InsertAllOnSubmit(newItems); 
dc.SubmitChanges(); 

INSERT語句像前一樣一個接一個地發送,但也許這可能更具可讀性?

一些其他的事情要問/考慮:

  • 什麼是目標表的索引的狀態?太多會減慢寫入速度。 *數據庫是簡單還是完全恢復模式?
  • 捕獲穿過網絡的SQL語句。在特定的查詢中針對SQL Server數據庫重播這些語句。我意識到你正在使用SQL Express,並且可能沒有SQL Profiler。使用context.Log = Console.Out;output your LINQ To SQL statements to the console。爲了方便起見,儘量使用SQL Profiler。
  • 捕獲的SQL語句是否與客戶端代碼執行相同的操作?如果是這樣,那麼性能問題在數據庫端。
+0

這是如何在內部工作的? – cjk 2010-09-20 11:40:41

+0

感謝您的輸入。重構完成。沒有太大的改善。除主鍵和自動生成的ID字段之外,沒有索引。 – JPReddy 2010-09-20 11:44:50

+0

@JPReddy:好東西。希望看到捕獲的+特定的SQL語句的性能。 – 2010-09-20 11:50:02

1

下面是如何在批量插入類添加到您的應用程序,這巨大改善了不錯的步行通過使用LINQ插入記錄的性能。

(提供所有的源代碼,隨時可以加入到自己的應用程序。)

http://www.mikesknowledgebase.com/pages/LINQ/InsertAndDeletes.htm

你就只需要實現三個轉變,以你的代碼,鏈接中提供的類。 祝你好運!