使用LINQ2SQL插入大量记录

Posted

技术标签:

【中文标题】使用LINQ2SQL插入大量记录【英文标题】:Using LINQ2SQL to insert a large number of records 【发布时间】:2010-11-01 18:31:26 【问题描述】:

我们有一个小型 c# 工具,我们将其用于解析数据文件、构建一些对象并将它们插入数据库。

逻辑本质上是。

string [] lines = File.ReadAllLines("C:\\Temp\\Data.dat")

foreach(string line in lines)

    MyDataObject obj = ParseObject(line);
    myDataContext.MyDataObjects.InsertOnSubmit(obj);


myDataContext.SubmitChanges();

一开始这很好,因为数据文件每天只有约 1000 行长 但最近这个文件已经增长到大约 30,000 行,而且这个过程变得非常缓慢。

SubmitChanges() 调用的一切都很好,但是一旦它开始转储过程 30,000 次插入数据库,它只是停止。作为测试,我模拟了 30,000 个插入语句并直接从 QA 运行它们。大约花了 8 分钟。

8 分钟后,C#/Linq 版本只完成了大约 25% 的插入。

有人对我如何优化这个有任何建议吗?

【问题讨论】:

【参考方案1】:

如果您正在编写大量同质数据,SqlBulkCopy 可能是更合适的工具,例如可能与CsvReader 一起读取行(因为SqlBulkCopy 可以接受IDataReader,这意味着您不'不必将所有 30k 行缓冲到内存中)。

如果数据是 CSV,这可以很简单:

using (CsvReader reader = new CsvReader(path))
using (SqlBulkCopy bcp = new SqlBulkCopy(CONNECTION_STRING))

    bcp.DestinationTableName = "SomeTable";
    bcp.WriteToServer(reader);

如果数据更复杂(不是 CSV),那么SimpleDataReader 可能有用 - 您只需对其进行子类化并添加代码来表示每行数据。

【讨论】:

Cheers Marc,沿着这条路走下去,在内存中建立了一个 DataTable 并将其传递给 BulkCopy 对象。不幸的是,它不是平面 CSV,在将其推送到服务器之前需要进行大量操作。谢谢【参考方案2】:

前段时间我也有同样的问题。我在数据库中插入 1000000 个新条目,我发现每 500 个调用 SubmitChanges 是最快的方法。

我不能保证当时的 500 行是有史以来最快的,我们的环境很奇怪......

【讨论】:

我怀疑这与单个 SQL 命令可以包含的最大参数数有关。我相信最大值是 2100 个参数。如果您要插入 4 列的记录,则大约 500 列的最佳值是有意义的。【参考方案3】:

您可能想尝试多线程方法。

    将记录集划分为较小的大小(每个 1000 条?),将它们放入堆栈中 拥有一个类,该类将从堆栈顶部获取记录集并使用多线程类开始插入它,该类会打开 DataContext 并自行插入。 在插入时,会为下一组记录打开第二个类 内部逻辑决定一次可以运行多少个插入(5 个?10 个?)

这可能允许插入比仅仅每隔几条记录运行一次 SubmitChanges() 更快,因为多个插入可以同时发生。

【讨论】:

【参考方案4】:

这是一项数据库任务,应通过 SSIS 并使用批量插入来完成。

我可以在几秒或几毫秒内插入 30,000 条记录(取决于列数和数据映射的复杂程度)。我有超过一百万条记录的导入,这些记录插入的时间比您一次循环遍历记录所花费的时间要少。我什至有一个 2000 万条记录文件只需要 16 分钟。

【讨论】:

是的,这很棒。然而这是不可行的。对于初学者来说,它在 SQL 2000 上,所以没有 ssis,这是 DTS 领域。其次,数据是从附属公司提交给我们的业务部门的。而且我很乐意为他们提供一个需要 2 分钟即可运行以上传数据的工具,而不是让他们完全访问数据库,以便他们可以与企业经理混在一起。 你可以用 DTS 做到这一点。然后让您的应用调用 DTS 包。或者使用批量插入在存储过程中执行此操作,并让您的应用调用该过程。【参考方案5】:

老问题,但在寻找我自己的解决方案后,我遇到了this code project article,这非常好。基本上使用 Linq2Sql 属性来构建一个 DataTable,然后使用 SQLBulkCopy 插入,这比基本的 Linq2Sql 实现快得多。文章中的代码可能需要进行一些清理,并且可能会在使用外键的更复杂的场景中失败(我的模型中没有外键,尽管数据库中有)但非常适合我的需求。

【讨论】:

以上是关于使用LINQ2SQL插入大量记录的主要内容,如果未能解决你的问题,请参考以下文章

LINQ2SQL 事务的性能

需要使用实体框架将大量记录插入数据库

JOOQ 插入到具有大量记录的选择中

在两个索引表上使用组和连接进行单独 LINQ2SQL 选择与一个组合选择的性能

如何在将大量数据插入数据库之前检查记录是不是存在?

mybatis批量插入oracle大量数据记录性能问题解决