我的代码中有这样的东西(.Net 2.0,MS SQL)

SqlConnection connection = new SqlConnection(@"Data Source=localhost;Initial
Catalog=DataBase;Integrated Security=True");
  connection.Open();

  SqlCommand cmdInsert = connection.CreateCommand();
  SqlTransaction sqlTran = connection.BeginTransaction();
  cmdInsert.Transaction = sqlTran;

  cmdInsert.CommandText =
     @"INSERT INTO MyDestinationTable" +
      "(Year, Month, Day, Hour,  ...) " +
      "VALUES " +
      "(@Year, @Month, @Day, @Hour, ...) ";

  cmdInsert.Parameters.Add("@Year", SqlDbType.SmallInt);
  cmdInsert.Parameters.Add("@Month", SqlDbType.TinyInt);
  cmdInsert.Parameters.Add("@Day", SqlDbType.TinyInt);
  // more fields here
  cmdInsert.Prepare();

  Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);

  StreamReader reader = new StreamReader(stream);
  char[] delimeter = new char[] {' '};
  String[] records;
  while (!reader.EndOfStream)
  {
    records = reader.ReadLine().Split(delimeter, StringSplitOptions.None);

    cmdInsert.Parameters["@Year"].Value = Int32.Parse(records[0].Substring(0, 4));
    cmdInsert.Parameters["@Month"].Value = Int32.Parse(records[0].Substring(5, 2));
    cmdInsert.Parameters["@Day"].Value = Int32.Parse(records[0].Substring(8, 2));
    // more here complicated stuff here
    cmdInsert.ExecuteNonQuery()
  }
  sqlTran.Commit();
  connection.Close();

cmdInsert.ExecuteNonQuery() 注释掉此代码执行时间不到 2 秒。对于 SQL 执行,需要 1m 20 秒。大约有 50 万条记录。表之前已清空。类似功能的 SSIS 数据流任务大约需要 20 秒。

  • 批量插入 曾是 不是一个选项(见下文)。在这次导入过程中我做了一些奇特的事情。
  • 我的测试机器是 Core 2 Duo,内存为 2 GB。
  • 在任务管理器中查看时,CPU 并未完全利用。IO似乎也没有被充分利用。
  • 架构非常简单:一张以 AutoInt 作为主索引且少于 10 个整数、微小整数和字符 (10) 的表。

经过一些回答后,我发现可以执行 从内存中批量复制!我拒绝使用批量复制,因为我认为它必须从文件中完成......

现在我使用这个,大约需要 20 秒(就像 SSIS 任务)

  DataTable dataTable = new DataTable();

  dataTable.Columns.Add(new DataColumn("ixMyIndex", System.Type.GetType("System.Int32")));   
  dataTable.Columns.Add(new DataColumn("Year", System.Type.GetType("System.Int32")));   
  dataTable.Columns.Add(new DataColumn("Month", System.Type.GetType("System.Int32")));
  dataTable.Columns.Add(new DataColumn("Day", System.Type.GetType("System.Int32")));
 // ... and more to go

  DataRow dataRow;
  object[] objectRow = new object[dataTable.Columns.Count];

  Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);

  StreamReader reader = new StreamReader(stream);
  char[] delimeter = new char[] { ' ' };
  String[] records;
  int recordCount = 0;
  while (!reader.EndOfStream)
  {
    records = reader.ReadLine().Split(delimeter, StringSplitOptions.None);

    dataRow = dataTable.NewRow();
    objectRow[0] = null; 
    objectRow[1] = Int32.Parse(records[0].Substring(0, 4));
    objectRow[2] = Int32.Parse(records[0].Substring(5, 2));
    objectRow[3] = Int32.Parse(records[0].Substring(8, 2));
    // my fancy stuf goes here

    dataRow.ItemArray = objectRow;         
    dataTable.Rows.Add(dataRow);

    recordCount++;
  }

  SqlBulkCopy bulkTask = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, null);
  bulkTask.DestinationTableName = "MyDestinationTable"; 
  bulkTask.BatchSize = dataTable.Rows.Count;
  bulkTask.WriteToServer(dataTable);
  bulkTask.Close();
有帮助吗?

解决方案

不要单独插入每条记录,而是尝试使用 SQL批量复制 类一次批量插入所有记录。

创建一个DataTable并将所有记录添加到DataTable中,然后使用 SQL批量复制.写入服务器 一次批量插入所有数据。

其他提示

是否需要交易?使用事务比简单命令需要更多的资源。

另外,如果您确定插入的值是正确的,则可以使用 BulkInsert。

对于 50 万条记录来说,1 分钟听起来相当合理。每 0.00012 秒就有一个记录。

该表有索引吗?如果可以选择的话,在批量插入后移除这些并重新应用它们将提高插入件的性能。

对我来说,每秒处理 8,333 条记录似乎并不不合理……您期望什么样的吞吐量?

如果您需要更好的速度,您可以考虑实施批量插入:

http://msdn.microsoft.com/en-us/library/ms188365.aspx

如果某种形式的批量插入不可行,另一种方法是多线程,每个线程都有自己的数据库连接。

当前系统的问题是,您对数据库进行了 500,000 次往返,并且正在等待第一个往返完成,然后再开始下一个往返 - 任何类型的延迟(即计算机之间的网络)都意味着大多数你的时间都花在了等待上。

如果您可以拆分作业,也许使用某种形式的生产者/消费者设置,您可能会发现可以更好地利用所有资源。

但是,要做到这一点,您将不得不丢失一个重要的事务 - 否则第一个写入器线程将阻塞所有其他线程,直到其事务完成。您仍然可以使用事务,但您必须使用许多小事务,而不是 1 个大事务。

SSIS 会很快,因为它使用批量插入方法 - 首先完成所有复杂的处理,生成要插入的最终数据列表,并同时将其全部提供给批量插入。

我假设大约需要 58 秒的时间是物理插入 500,000 条记录 - 因此每秒大约插入 10,000 条记录。在不知道数据库服务器计算机的规格的情况下(我看到您使用的是本地主机,因此网络延迟不应该成为问题),很难说这是好、坏还是糟糕。

我会查看您的数据库架构 - 表上是否有一堆索引在每次插入后都必须更新?这可能来自其他带有外键引用您正在处理的表的表。SQL Server 中内置有 SQL 分析工具和性能监控工具,但我从未使用过它们。但它们可能会出现诸如锁之类的问题。

首先对所有记录对数据进行一些奇特的处理。然后批量插入它们。

(因为你没有在插入后进行选择..我没有看到在 BulkInsert 之前对数据应用所有操作的问题

如果我必须猜测,我首先要查找的是 tbTrafficLogTTL 表上的索引太多或类型错误。如果不查看表的架构定义,我真的不能说,但我在以下情况下遇到了类似的性能问题:

  1. 主键是 GUID,主索引是 CLUSTERED。
  2. 一组字段上有某种唯一索引。
  3. 表上的索引太多。

当您开始为 50 万行数据建立索引时,创建和维护索引所花费的时间就会增加。

我还要指出的是,如果您可以选择将年、月、日、小时、分钟、秒字段转换为单个 datetime2 或时间戳字段,那么您应该这样做。您为数据架构增加了很多复杂性,却没有任何好处。我什至考虑使用这样的拆分字段结构的唯一原因是,如果您正在处理一个预先存在的数据库模式,该模式因任何原因都无法更改。在这种情况下,你就很糟糕了。

我在上一份合同中也遇到了类似的问题。您将执行 500,000 次 SQL 来插入数据。为了显着提高性能,您需要研究 SQL 命名空间中的 BulkInsert 方法。实施批量导入后,我的“重新加载”过程从恢复几十个表的 2 个多小时缩短到 31 秒。

最好使用 bcp 命令之类的命令来完成此操作。如果不可用,上面关于使用 BULK INSERT 的建议是您最好的选择。您将对数据库进行 500,000 次往返,并将 500,000 个条目写入日志文件,更不用说需要分配给日志文件、表和索引的任何空间。

如果您的插入顺序与聚集索引不同,您还必须处理重新组织磁盘上物理数据所需的时间。这里有很多变量可能会使您的查询运行速度比您希望的慢。

对于从代码往返的单个插入来说,每秒约 10,000 个事务并不可怕/

BULK INSERT = 来自权限的 bcp

您可以批量插入以减少往返Sqldataadaptor.updatebatchsize = 10000提供50次往返旅行

不过你还有 500k 插入...

文章

微软软件定义网络

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top