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?
fonte
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?
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.
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.)
fonte
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.
fonte
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.
fonte
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!
fonte
Você pode querer:
Dependendo de quão bem o servidor é dimensionado (é definitivamente aceitável com
PostgreSQl
,Oracle
eMSSQL
), faça o procedimento acima com vários threads e várias conexões.fonte
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.
fonte
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.
Eu diria que a linha é o caminho a seguir :)
fonte
É 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.
fonte
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?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:
offcourse, você deve ligá-lo novamente após as inserções:
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.
fonte