如何改变缓慢参数化的插入到快速批量复制(甚至从内存中)(How to change slow par

2019-08-01 03:41发布

我成才像这样在我的代码(.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执行需要1米20秒。 大约有0.5畅想记录。 表之前排空。 的相似的功能SSIS数据流任务需要大约20秒。

  • BULK INSERT 不是一种选择(见下文)。 我这个导入过程中做了一些花哨的东西。
  • 我的测试机是Core 2 Duo处理器与2 GB RAM。
  • 当寻找在任务管理器的CPU是不能完全untilized。 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();

Answer 1:

相反,单独将每个记录的,请尝试使用使用SqlBulkCopy类批量一次插入所有记录。

创建数据表和所有的记录添加到DataTable,然后使用SqlBulkCopy的 。 在WriteToServer批量一次插入的所有数据。



Answer 2:

需要进行交易? 使用事务需要比简单的命令,很多更多的资源。

此外,如果你是肯定的,那插入的值是corect,您可以使用BulkInsert。



Answer 3:

1分钟听起来0.5万条记录相当合理。 这是一个创纪录的每0.00012秒。

请问表有什么索引? 批量插入后删除这些并重新应用它们将提高刀片的性能,如果这是一个选项。



Answer 4:

它似乎没有道理的我来处理8,333记录每秒......什么样的吞吐量你期待?



Answer 5:

如果你需要更好的速度,你可能需要考虑执行批量插入:

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



Answer 6:

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

与当前系统的问题是,你得为50个万名往返数据库,并正在等待第一次往返开始下一个之前完成 - 任何类型的延迟(即机器之间的网络),将意味着多数你的时间都花在等待。

如果你可以分割的工作,或许使用某种形式的生产者/消费者安装的,你可能会发现,你可以得到所有资源的更利用率。

然而,要做到这一点,你将不得不失去一个伟大的交易 - 否则,直到其交易完成后第一个作家线程将阻止所有其他人。 您仍然可以使用交易,但你必须要使用大量的小的,而不是1大的。

首先做所有复杂的处理,生成数据的最终名单中插入并给它所有在同一时间批量插入 - 因为它使用了大容量的插入方法的SSIS将会很快。



Answer 7:

我认为什么走的是约58秒50所记录的物理插入 - 所以你得到约10000插入第二。 不知道你的数据库服务器(我看你是使用本地主机,所以网络延迟不应该是一个问题)的规格,它是很难说,如果这是好的,坏的,或深不可测。

我想看看你的数据库模式 - 在那里有每次插入后要更新的表一堆指标? 这可能是从外键引用你的工作表上的其它表。 有内置到SQL Server SQL分析工具和性能监控设施,但我从来没有使用过。 但是,他们可能会出现类似问题的锁,事情就是这样的。



Answer 8:

先做上的数据花哨的东西,所有的记录。 然后批量插入。

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



Answer 9:

如果我猜的话,我会寻找的第一件事是过多还是一种错误的对tbTrafficLogTTL表中的索引。 不看表的架构定义,我真的不能说,但我已经经历过类似的性能问题时:

  1. 主键是一个GUID和主索引是集群。
  2. 有某种唯一索引上的一组字段。
  3. 有在桌子上太多的索引。

当您启动索引五十万行数据,所花费的时间来创建和维护索引加起来。

我也注意到,如果您有任何选项年,月,日,时,分,秒字段转换成一个单一的DATETIME2或时间戳字段,你应该。 您要添加大量的复杂性,以您的数据架构,对于没有收获。 我甚至会考虑使用分离式场结构一样,唯一的理由是,如果你正在处理不能以任何理由来改变预先存在的数据库架构。 在这种情况下,它吮吸是你。



Answer 10:

我在去年的合同也有类似的问题。 你让50万人次到SQL插入您的数据。 对于业绩大幅增加,你要调查在SQL命名空间中的BulkInsert方法。 我不得不从2+小时就恢复了几十桌的降幅达到31秒一次我实现了批量导入“重装”过程。



Answer 11:

这可以最好地使用类似bcp命令来实现。 如果这是不可用的,上面关于使用BULK INSERT的建议是你最好的选择。 你让50万个往返到数据库,并编写50万个条目的日志文件,更不用说需要被分配到日志文件,该表中的任何空间和索引。

如果你在这是从你的聚集索引的不同的顺序插入,你还必须应对的时候需要重新组织磁盘上的物理数据。 有很多在这里变量也可能会被使你的查询运行得更慢比你想它。

〜每秒10,000个事务并不可怕单个刀片来从代码双向传递/



Answer 12:

BULK INSERT从权限= BCP

你可以批量插入物,以减少往返SQLDataAdaptor.UpdateBatchSize = 10000给出了50个往返

你还有500k的插入,但...

文章

MSDN



文章来源: How to change slow parametrized inserts into fast bulk copy (even from memory)