O que é mais rápido: vários INSERTs únicos ou um INSERT de várias linhas?

184

Estou tentando otimizar uma parte do meu código que insere dados no MySQL. Devo encadear INSERTs para criar um enorme INSERT de várias linhas ou vários INSERTs separados mais rapidamente?

dusoft
fonte

Respostas:

287

https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html

O tempo necessário para inserir uma linha é determinado pelos seguintes fatores, em que os números indicam proporções aproximadas:

  • Conexão: (3)
  • Enviando consulta ao servidor: (2)
  • Consulta de análise: (2)
  • Inserindo linha: (1 × tamanho da linha)
  • Inserindo índices: (1 × número de índices)
  • Encerramento: (1)

Por isso, deve ser óbvio que o envio de uma declaração grande economizará uma sobrecarga de 7 por declaração de inserção, que na leitura adicional do texto também diz:

Se você estiver inserindo muitas linhas do mesmo cliente ao mesmo tempo, use as instruções INSERT com várias listas VALUES para inserir várias linhas por vez. Isso é consideravelmente mais rápido (muitas vezes mais rápido em alguns casos) do que usar instruções INSERT separadas de linha única.

mbarkhau
fonte
27
Como essa resposta se aplica se vários INSERTs únicos estiverem na mesma transação do banco de dados?
Aperte
2
Quantas linhas posso inserir por vez usando a instrução de inserção única. é possível inserir 10.000 linhas por vez?
Naresh Ramoliya
10
@Pinch O uso de uma transação ao realizar ~ 1.5k upserts (inserção / atualizações) diminuiu o tempo que a operação levou de ~ 1,5 segundos para ~ 0,2 segundos. Ou, em outras palavras, tornou-o 86% mais rápido em comparação às pastilhas de linha única. Droga.
Fgblomqvist
1
Nota: Parece ser muito diferente no MSSQL: stackoverflow.com/questions/8635818/…
marsze
Que tal usar a Declaração Preparada para inserir várias inserções únicas repetitivas?
Priyabagus # 14/18
151

Eu sei que estou respondendo a essa pergunta quase dois anos e meio depois que ela foi feita, mas eu só queria fornecer alguns dados concretos de um projeto no qual estou trabalhando agora, que mostra que realmente fazer vários blocos VALUE por inserção é MUITO mais rápido que as instruções INSERT do bloco VALUE único seqüencial.

O código que eu escrevi para esse benchmark em C # usa ODBC para ler dados na memória de uma fonte de dados MSSQL (~ 19.000 linhas, todas lidas antes do início de qualquer gravação) e o conector do MySql .NET (Mysql.Data. *) Para INSIRA os dados da memória em uma tabela em um servidor MySQL por meio de instruções preparadas. Foi escrito de forma a permitir que eu ajustasse dinamicamente o número de blocos VALUE por INSERT preparado (ou seja, insira n linhas por vez, onde eu poderia ajustar o valor de n antes de uma execução). Também executei o teste várias vezes para cada n.

A execução de blocos únicos de VALUE (por exemplo, 1 linha por vez) levou de 5,7 a 5,9 segundos para ser executada. Os outros valores são os seguintes:

2 linhas por vez: 3,5 - 3,5 segundos
5 linhas por vez: 2,2 - 2,2 segundos
10 linhas por vez: 1,7 - 1,7 segundos
50 linhas por vez: 1,17 - 1,18 segundos
100 linhas por vez: 1,1 - 1,4 segundos
500 linhas por vez: 1,1 - 1,2 segundos
1000 linhas por vez: 1,17 - 1,17 segundos

Portanto, sim, mesmo o agrupamento de 2 ou 3 gravações fornece uma melhoria dramática na velocidade (tempo de execução reduzido por um fator de n), até chegar a algo entre n = 5 en = 10, quando a melhoria diminui acentuadamente, e em algum lugar na faixa de n = 10 a n = 50, a melhoria se torna insignificante.

Espero que ajude as pessoas a decidir sobre (a) se deve usar a ideia de multipreparação e (b) quantos blocos VALUE criar por instrução (supondo que você queira trabalhar com dados que sejam grandes o suficiente para fazer com que a consulta ultrapasse o tamanho máximo da consulta para o MySQL, que acredito ter 16 MB por padrão em muitos lugares, possivelmente maior ou menor, dependendo do valor de max_allowed_packet definido no servidor.)

Jon Kloske
fonte
1
Solicitação de esclarecimento: é o seu tempo "segundos por linha" ou "total de segundos".
EngrStudent
3
Total de segundos - portanto, segundos por linha é aquele dividido pelas ~ 19.000 linhas. Embora esse seja um número pequeno, talvez linhas / segundo seja uma métrica melhor se você estiver procurando por um número facilmente comparável.
precisa saber é o seguinte
Aliás, há um exemplo de código .NET para a abordagem que eu descrevi acima sobre esta resposta relacionada meu: stackoverflow.com/questions/25377357/...
Jon Kloske
18

Um fator importante será o uso de um mecanismo transacional e a confirmação automática ativada.

A confirmação automática está ativada por padrão e você provavelmente deseja deixá-la ativada; portanto, cada inserção que você faz faz sua própria transação. Isso significa que, se você fizer uma inserção por linha, confirmará uma transação para cada linha.

Assumindo um único encadeamento, isso significa que o servidor precisa sincronizar alguns dados em disco para TODAS AS LINHAS. Ele precisa aguardar que os dados atinjam um local de armazenamento persistente (esperamos que a memória RAM suportada por bateria no seu controlador RAID). Isso é inerentemente bastante lento e provavelmente se tornará o fator limitante nesses casos.

Obviamente, estou assumindo que você está usando um mecanismo transacional (geralmente innodb) E que não ajustou as configurações para reduzir a durabilidade.

Também estou assumindo que você está usando um único thread para fazer essas inserções. O uso de vários threads atrapalha um pouco as coisas porque algumas versões do MySQL têm confirmação de grupo de trabalho no innodb - isso significa que vários threads que fazem suas próprias confirmações podem compartilhar uma única gravação no log de transações, o que é bom porque significa menos sincronizações para o armazenamento persistente .

Por outro lado, o resultado é que você REALMENTE QUER USAR inserções de várias linhas.

Há um limite sobre o qual é contraproducente, mas na maioria dos casos são pelo menos 10.000 linhas. Portanto, se você agrupá-las em até 1.000 linhas, provavelmente estará seguro.

Se você estiver usando o MyISAM, há muitas outras coisas, mas não vou aborrecê-las. Paz.

MarkR
fonte
1
Existe alguma razão para ser contraproducente após um ponto? Eu já vi isso acontecer antes, mas não tinha certeza do porquê.
Dhruv Gairola
1
Você sabe se há algum ponto no lote de inserções do MySQL ao usar transações . Estou apenas me perguntando se posso me poupar do problema de ter que gerar o comando SQL com vários valores se minha biblioteca subjacente (Java JDBC - mysql-connector-java-5.1.30) não estiver realmente comprometendo até que eu solicite.
RTF
@RTF Acho que você precisará executar um pequeno teste para determinar esse comportamento em sua situação, pois é altamente específico para implementação, mas em muitos casos sim, as transações devem fornecer ganhos de desempenho semelhantes.
Jasmine Hegman
9

Envie o máximo de inserções pelo fio ao mesmo tempo possível. A velocidade real da inserção deve ser a mesma, mas você verá ganhos de desempenho com a redução da sobrecarga da rede.

RC.
fonte
7

Em geral, quanto menor o número de chamadas para o banco de dados, melhor (o que significa mais rápido, mais eficiente), portanto, tente codificar as inserções de forma a minimizar os acessos ao banco de dados. Lembre-se, a menos que você esteja usando um pool de conexões, cada acesso ao banco de dados deve criar uma conexão, executar o sql e, em seguida, derrubar a conexão. Um pouco de sobrecarga!

ennuikiller
fonte
e se a conexão persistente for usada?
dusoft 24/11/2009
6
Ainda há sobrecarga. O tempo de trânsito sozinho (de e para cada inserção separada) será rapidamente perceptível se você estiver fazendo milhares de inserções.
RC.
4

Você pode querer:

  • Verifique se a confirmação automática está desativada
  • Conexão aberta
  • Envie vários lotes de inserções em uma única transação (tamanho de cerca de 4000 a 10000 linhas? Você vê)
  • Fechar conexão

Dependendo de quão bem o servidor é dimensionado (é definitivamente aceitável com PostgreSQl, Oraclee MSSQL), faça o procedimento acima com vários threads e várias conexões.

Antibarbie
fonte
3

Em geral, várias inserções serão mais lentas devido à sobrecarga da conexão. Fazer várias inserções ao mesmo tempo reduzirá o custo de sobrecarga por inserção.

Dependendo do idioma que você está usando, é possível criar um lote na sua linguagem de programação / script antes de ir para o banco de dados e adicionar cada inserção ao lote. Então você seria capaz de executar um lote grande usando uma operação de conexão. Aqui está um exemplo em Java.

Chris Williams
fonte
3

MYSQL 5.5 Uma instrução de inserção sql levou de ~ 300 a ~ 450ms. enquanto as estatísticas abaixo são para várias inserções inseridas em linha.

(25492 row(s) affected)
Execution Time : 00:00:03:343
Transfer Time  : 00:00:00:000
Total Time     : 00:00:03:343

Eu diria que a linha é o caminho a seguir :)

A_01
fonte
0

É ridículo o quão ruim o MySQL e o MariaDB são otimizados quando se trata de inserções. Testei o mysql 5.7 e o mariadb 10.3, nenhuma diferença real neles.

Eu testei isso em um servidor com discos NVME, 70.000 IOPS, taxa de transferência seq de 1,1 GB / s e isso é possível full duplex (leitura e gravação).
O servidor também é um servidor de alto desempenho.
Deu 20 GB de RAM.
O banco de dados completamente vazio.

A velocidade que recebi foi de 5000 inserções por segundo ao fazer inserções de várias linhas (tentei com pedaços de dados de 1 MB a 10 MB)

Agora a dica:
se eu adicionar outro thread e inserir nas mesmas tabelas, de repente tenho 2x5000 / s. Mais um thread e eu tenho 15000 total / s

Considere o seguinte: ao fazer inserções do ONE, significa que você pode gravar sequencialmente no disco (com exceções nos índices). Ao usar threads, na verdade, você prejudica o desempenho possível, porque agora ele precisa fazer muito mais acessos aleatórios. Mas a verificação da realidade mostra que o mysql está tão otimizado que os threads ajudam muito.

O desempenho real possível com esse servidor é provavelmente milhões por segundo, a CPU está ociosa e o disco está ocioso.
A razão é claramente que o mariadb, assim como o mysql, possui atrasos internos.

John
fonte
@Craftables você precisa de desenvolvimento externo, isso não pode ser feito no mysql. Threads significa que você usa várias conexões com o servidor, divide a consulta em vários blocos (por exemplo, dividindo-a em partes pares pela chave primária). Consegui obter até 10.000 vezes o desempenho usando esse método em tabelas muito grandes. As consultas que seriam executadas por 40.000 segundos podem ser concluídas em 2 a 3 minutos, se você usar vários threads e seu mysql estiver altamente otimizado.
John John
@ John Interessante e pode ter alguns aplicativos realmente bons ... mas ... Se você dividir a consulta em vários blocos, como lida com as transações? E considere também o seguinte cenário: A tabela x possui uma coluna 'parent_id' que se relaciona à mesma tabela 'id'. Em algum lugar dentro de seus dados, você INSERIR EM x ( id, parent_id) VALUES (1, NULL). Um dos próximos conjuntos de valores vincula a essa linha. Se você dividir em pedaços e esse conjunto chegar a outro pedaço, ele poderá ser processado antes do primeiro, falhando durante todo o processo. Alguma idéia de como lidar com isso?
Zozo
@zozo isso é útil para inserções em massa e consultas em massa. As transações arruinariam o desempenho de qualquer maneira, pois incluem bastante buffer de dados. Mas você também pode usar transações em inserções ou consultas multiencadeadas.
João
-2

várias inserções são mais rápidas, mas devem ser aplicadas. outro thrik é desabilitar restrições de cheques temporários, fazer inserções muito mais rápidas. Não importa que sua mesa tenha ou não. Por exemplo, teste a desativação de chaves estrangeiras e aproveite a velocidade:

SET FOREIGN_KEY_CHECKS=0;

offcourse, você deve ligá-lo novamente após as inserções:

SET FOREIGN_KEY_CHECKS=1;

Essa é uma maneira comum de inserir dados enormes. a integridade dos dados pode ser interrompida e você deve cuidar disso antes de desativar as verificações de chave estrangeira.

MSS
fonte
1
Não faço idéia por que as pessoas votaram nisso por duas razões: 1. Não tem nada a ver com a pergunta 2. É uma péssima idéia (com algumas exceções - como dumping ou mudanças estruturais de temperatura -, mas ruim em geral). As verificações existem por um motivo: Elas existem para garantir a consistência dos dados. As coisas ficam mais lentas porque garantem que você não insira ou altere dados que não deveria. Tente otimizar as consultas da maneira certa; em qualquer ambiente crítico para os negócios, isso significaria a morte do aplicativo, pois, independentemente de quão cuidadoso você seja, as coisas falharão em algum momento.
zozo
1
talvez, mas essa opção seja extremamente eficaz na importação de tabelas grandes e muito prática e possa dar a algumas pessoas uma idéia de como elas podem tornar a inserção de dados muito mais rápida.
MSS