2010-09-20 88 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

你就只需要实现三个转变,以你的代码,链接中提供的类。 祝你好运!