Pergunta

Eu tinha someting como esta no meu código (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();

Com cmdInsert.ExecuteNonQuery () comentado este executa o código em menos de 2 segundos. Com a execução SQL leva 1m 20 seg. Há cerca de 0,5 milion registros. Tabela é esvaziado antes. dados SSIS fluir tarefa de funcionalidade semelhante leva cerca de 20 segundos.

  • inserção em massa foi não é uma opção (ver abaixo). Eu fiz algumas coisas extravagantes durante esta importação.
  • A minha máquina de teste é Core 2 Duo com 2 GB RAM.
  • Ao olhar em Task Manager CPU não estava totalmente untilized. IO parecia também não ser totalmente utilizado.
  • esquema é simples como o inferno: uma tabela com AutoInt como índice primário e menos de 10 ints, pequenas ints e caracteres (10)
  • .

Depois de algumas respostas aqui eu descobri que é possível executar cópia em massa a partir da memória ! Eu estava se recusando a usar cópia em massa beacuse eu pensei que tem que ser feito a partir do arquivo ...

Agora eu uso este e leva aroud 20 seg (como tarefa 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();
Foi útil?

Solução

Em vez de inserir cada registro individualmente, tente usar a SqlBulkCopy classe para maior inserção todos os registros de uma só vez.

Criar um DataTable e adicionar todos os seus registros à tabela de dados, e então usar SqlBulkCopy . WriteToServer para inserção em massa todos os dados ao mesmo tempo.

Outras dicas

É necessária a transação? Usando necessidade de transação muito mais recursos do que comandos simples.

Além disso Se você tem certeza, que os valores inseridos são corect, você pode usar um BulkInsert.

1 minuto soa bastante razoável para 0,5 milhões de registros. É um recorde a cada 0,00012 segundos.

A mesa tem nenhum índices? Removendo estes e reaplicando-los após a inserção em massa iria melhorar o desempenho das inserções, se isso é uma opção.

Não parece razoável me para processar 8.333 registros por segundo ... que tipo de rendimento que você está esperando?

Se você precisar de mais velocidade, você pode considerar a implementação de inserção em massa:

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

Se alguma forma de inserção em massa não é uma opção, a outra maneira seria vários segmentos, cada um com sua própria conexão com o banco de dados.

O problema com o sistema atual é que você tem 500.000 ida e volta para o banco de dados, e estão à espera para a primeira ida e volta para completo antes de iniciar o próximo - qualquer tipo de latência (ou seja, uma rede entre as máquinas) significará que a maioria de seu tempo é gasto esperando.

Se você pode dividir o trabalho até, talvez usando alguma forma de configuração produtor / consumidor, você pode achar que você pode obter muito mais a utilização de todos os recursos.

No entanto, para fazer isso você terá que perder a única grande transação - caso contrário, o primeiro segmento escritor irá bloquear todos os outros até que sua transação seja concluída. Você ainda pode usar transações, mas você vai ter que usar um monte de pequenos em vez de um grande.

O SSIS vai ser rápido porque ele está usando o método bulk-insert -. Fazer o processamento de todo o complicado primeiro, gerar a lista final dos dados para inserir e dar tudo ao mesmo tempo para aumentar a massa-insert

Eu supor que o que está levando os cerca de 58 segundos é a inserção física de 500.000 registros - para que você está recebendo cerca de 10.000 inserções por segundo. Sem saber as especificações de sua máquina servidor de banco de dados (eu vejo que você está usando localhost, assim atrasos na rede não deve ser um problema), é difícil dizer se isso é bom, ruim ou péssima.

Eu olhava para seu esquema de banco de dados - estão lá um bando de índices na tabela que têm de ser atualizada após cada inserção? Esta poderia ser a partir de outras tabelas com chaves estrangeiras que fazem referência a tabela que você está trabalhando. Há SQL profiling ferramentas e instalações de monitoramento de desempenho construídos em SQL Server, mas eu nunca usei-los. Mas eles podem aparecer problemas como fechaduras, e coisas desse tipo.

Faça o material extravagante sobre os dados, em todos os registos em primeiro lugar. Então Bulk-inseri-los.

(desde que você não está fazendo seleciona após uma inserção .. eu não vejo o problema de aplicar todas as operações sobre os dados antes da BulkInsert

Se eu tivesse que adivinhar, a primeira coisa que eu iria procurar são muitos ou o tipo errado de índices na tabela de tbTrafficLogTTL. Sem olhar para a definição de esquema para a tabela, eu realmente não posso dizer, mas eu tenho experimentado problemas de desempenho semelhantes quando:

  1. A chave primária é um GUID e o índice primário está agrupado.
  2. Há algum tipo de índice exclusivo em um conjunto de campos.
  3. Existem muitos índices na tabela.

Quando você começar a indexação de meio milhão de linhas de dados, o tempo gasto para criar e manter índices acrescenta-se.

Além disso, vou notar que, se você tem qualquer opção de converter o ano, mês, dia, hora, minuto, campos de segunda em um único datetime2 ou timestamp campo, você deve. Você está adicionando um monte de complexidade para a sua arquitetura de dados, para nenhum ganho. A única razão que eu sequer contemplar usando uma estrutura split-campo como isto é, se você está lidando com um esquema de banco de dados pré-existente que não pode ser alterado por qualquer motivo. Nesse caso, é um saco ser você.

Eu tive um problema semelhante no meu último contrato. Você está fazendo 500.000 viagens para SQL para inserir seus dados. Para um aumento dramático no desempenho, que pretende investigar o método BulkInsert no namespace SQL. Eu tive processos "reload", que passou de 2+ horas para restaurar um par de dezenas de mesas de até 31 segundos uma vez que eu implementado importação em massa.

Esta tarefa poderá ser realizada utilizando algo como o comando bcp. Se isso não estiver disponível, as sugestões acima sobre o uso de BULK INSERT são sua melhor aposta. Você está fazendo 500.000 ida e volta para o banco de dados e escrever 500.000 entradas nos arquivos de log, para não mencionar qualquer espaço que precisa ser alocado para o arquivo de log, a tabela e os índices.

Se você está inserindo em uma ordem que seja diferente do seu índice de cluster, você também tem que lidar com o tempo precisa para reorganizar os dados físicos no disco. Há uma série de variáveis ??aqui que poderia estar fazendo sua corrida consulta mais lento do que você gostaria que ele.

~ 10.000 transações por segundo não é terrível para inserções individuais próximos roundtripping a partir do código /

BULK INSERT = bcp de uma permissão

Você poderia lote as inserções para reduzir roundtrips SQLDataAdaptor.UpdateBatchSize = 10000 dá 50 viagens de ida

Você ainda tem 500k inserções embora ...

artigo

MSDN

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top