Che è più veloce: più INSERT singoli o un INSERT a più righe?


184

Sto cercando di ottimizzare una parte del mio codice che inserisce i dati in MySQL. Devo concatenare INSERT per creare un enorme INSERT a più righe o più INSERT separati separati più velocemente?

Risposte:


287

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

Il tempo richiesto per l'inserimento di una riga è determinato dai seguenti fattori, in cui i numeri indicano proporzioni approssimative:

  • Connessione: (3)
  • Invio query al server: (2)
  • Query di analisi: (2)
  • Inserimento riga: (1 × dimensione della riga)
  • Inserimento di indici: (1 × numero di indici)
  • Chiusura: (1)

Da ciò dovrebbe essere ovvio che l'invio di un'istruzione di grandi dimensioni consente di risparmiare un overhead di 7 per istruzione insert, che nella lettura ulteriore del testo dice anche:

Se si inseriscono più righe dallo stesso client contemporaneamente, utilizzare le istruzioni INSERT con più elenchi VALUES per inserire più righe contemporaneamente. Ciò è notevolmente più veloce (molte volte più veloce in alcuni casi) rispetto all'uso di istruzioni INSERT separate a riga singola.


27
Come si applica questa risposta se più INSERT singoli si trovano nella stessa transazione del database?
Pizzica il

2
Quante righe posso inserire contemporaneamente usando una singola istruzione insert. mi consente di inserire 10000 righe alla volta?
Naresh Ramoliya,

10
@Pinch L'utilizzo di una transazione durante gli aggiornamenti ~ 1.5k (inserimento / aggiornamento) ha ridotto il tempo necessario da ~ 1,5 secondi a ~ 0,2 secondi. O in altre parole, ha reso l'86% più veloce rispetto agli inserti a riga singola. Dannazione.
fgblomqvist

1
Nota: Sembra essere molto diverso in MSSQL: stackoverflow.com/questions/8635818/...
marsze

Che ne dite di usare la dichiarazione preparata per l'inserimento di inserti multipli multipli ripetitivi?
priyabagus,

151

So che sto rispondendo a questa domanda quasi due anni e mezzo dopo che è stata posta, ma volevo solo fornire alcuni dati concreti da un progetto a cui sto lavorando in questo momento che dimostra che effettivamente fare più blocchi VALUE per inserto è MOLTO più veloce delle istruzioni INSERT in blocco singolo VALUE sequenziali.

Il codice che ho scritto per questo benchmark in C # utilizza ODBC per leggere i dati in memoria da un'origine dati MSSQL (~ 19.000 righe, tutte sono lette prima dell'inizio della scrittura), e il MySql .NET Connector (Mysql.Data. *) Roba per INSERIRE i dati dalla memoria in una tabella su un server MySQL tramite istruzioni preparate. È stato scritto in modo tale da consentirmi di regolare dinamicamente il numero di blocchi VALUE per INSERT preparato (ovvero, inserire n righe alla volta, in cui ho potuto regolare il valore di n prima di un'esecuzione). Ho anche eseguito il test più volte per ogni n.

L'esecuzione di blocchi VALUE singoli (ad es. 1 riga alla volta) ha richiesto 5,7 - 5,9 secondi. Gli altri valori sono i seguenti:

2 righe alla volta: 3,5 - 3,5 secondi
5 righe alla volta: 2,2 - 2,2 secondi
10 righe alla volta: 1,7 - 1,7 secondi
50 righe alla volta: 1,17 - 1,18 secondi
100 righe alla volta: 1,1 - 1,4 secondi
500 righe alla volta: 1,1 - 1,2 secondi
1000 righe alla volta: 1,17 - 1,17 secondi

Quindi sì, anche solo raggruppando 2 o 3 scritture insieme fornisce un notevole miglioramento della velocità (riduzione del tempo di esecuzione di un fattore di n), fino a quando non si arriva a un punto compreso tra n = 5 e n ​​= 10, a quel punto il miglioramento diminuisce notevolmente, e da qualche parte nell'intervallo da n = 10 a n = 50 il miglioramento diventa trascurabile.

Spero che possa aiutare le persone a decidere (a) se usare l'idea di multiprepare, e (b) quanti VALUE blocchi creare per ogni istruzione (supponendo che si desideri lavorare con dati che potrebbero essere abbastanza grandi da spingere la query oltre la dimensione massima della query per MySQL, che per impostazione predefinita è 16 MB in molti punti, forse più grande o più piccolo a seconda del valore di max_allowed_packet impostato sul server.)


1
Richiesta di chiarimento: è il tuo tempo "secondi per riga" o "secondi totali".
EngrStudent,

3
Totale dei secondi - quindi i secondi per riga sono divisi per ~ 19.000 righe. Sebbene sia un numero piccolo, quindi forse righe / secondo è una metrica migliore se stai cercando un numero facilmente confrontabile.
Jon Kloske,

Per inciso, c'è qualche esempio di codice .NET per l'approccio che ho descritto sopra su questa risposta correlata mia: stackoverflow.com/questions/25377357/...
Jon Kloske

18

Un fattore importante sarà se stai usando un motore transazionale e se hai un autocommit attivo.

L'autocommit è attivo per impostazione predefinita e probabilmente vorrai lasciarlo attivo; pertanto, ogni inserto che fai fa la propria transazione. Ciò significa che se esegui un inserimento per riga, eseguirai una transazione per ogni riga.

Supponendo un singolo thread, ciò significa che il server deve sincronizzare alcuni dati su disco per OGNI RIGA. Deve attendere che i dati raggiungano una posizione di memorizzazione persistente (si spera che il ram con batteria nel controller RAID). Questo è intrinsecamente piuttosto lento e probabilmente diventerà il fattore limitante in questi casi.

Naturalmente suppongo che stai utilizzando un motore transazionale (di solito innodb) E che non hai modificato le impostazioni per ridurre la durata.

Suppongo anche che tu stia usando un singolo thread per fare questi inserti. L'uso di più thread confonde un po 'le cose perché alcune versioni di MySQL hanno il commit del gruppo di lavoro in innodb - questo significa che più thread che eseguono i propri commit possono condividere una sola scrittura nel registro delle transazioni, il che è positivo perché significa meno sincronizzazioni con l'archiviazione persistente .

D'altro canto, il risultato è che VUOI REALMENTE UTILIZZARE gli inserti a più file.

C'è un limite oltre il quale diventa controproducente, ma nella maggior parte dei casi è di almeno 10.000 righe. Quindi se li lotti fino a 1.000 file, probabilmente sei al sicuro.

Se stai usando MyISAM, c'è un sacco di altre cose, ma non ti annoierò con quelle. Pace.


1
C'è qualche motivo per cui diventa controproducente dopo un punto? L'ho già visto accadere prima, ma non ero sicuro del perché.
Dhruv Gairola,

1
Sai se c'è qualche motivo nel raggruppare inserti MySQL quando si utilizzano le transazioni . Mi sto solo chiedendo se posso salvarmi il problema di dover generare il comando SQL multivalore se la mia libreria sottostante (Java JDBC - mysql-connettore-java-5.1.30) non si sta effettivamente impegnando fino a quando non lo dirò a.
RTF

@RTF Penso che dovrai eseguire un piccolo test per determinare quel comportamento nella tua situazione in quanto si tratta di un comportamento altamente implementativo specifico, ma in molti casi sì le transazioni dovrebbero fornire guadagni di prestazioni simili.
Jasmine Hegman,

9

Invia quanti più inserti sul filo contemporaneamente. La velocità di inserimento effettiva dovrebbe essere la stessa, ma vedrai miglioramenti delle prestazioni dalla riduzione del sovraccarico della rete.


7

In generale, minore è il numero di chiamate al database, migliore è (più veloce, più efficiente), quindi prova a codificare gli inserti in modo tale da ridurre al minimo gli accessi al database. Ricordare, a meno che non si stia utilizzando un pool di connessioni, ogni accesso al database deve creare una connessione, eseguire sql e quindi ridurre la connessione. Abbastanza un po 'di spese generali!


cosa succede se viene utilizzata la connessione persistente?
dusoft,

6
C'è ancora un sovraccarico. Il tempo di transito da solo (da e per ciascun inserto separato) sarà rapidamente percepibile se stai facendo migliaia di inserti.
RC.

4

Potresti volere :

  • Verificare che il commit automatico sia disattivato
  • Apri connessione
  • Invia più lotti di inserti in un'unica transazione (dimensioni di circa 4000-10000 righe? Vedi)
  • Connessione stretta

A seconda di quanto bene il tuo server si ridimensiona (è definitivamente ok con PostgreSQl, Oraclee MSSQL), fai la cosa sopra con più thread e connessioni multiple.


3

In generale, più inserti saranno più lenti a causa del sovraccarico della connessione. Fare più inserti contemporaneamente ridurrà il costo dell'overhead per inserto.

A seconda della lingua che stai usando, puoi eventualmente creare un batch nel tuo linguaggio di programmazione / scripting prima di andare al db e aggiungere ogni inserto al batch. Quindi sarà possibile eseguire un batch di grandi dimensioni utilizzando un'operazione di connessione. Ecco un esempio in Java.


3

MYSQL 5.5 Un'istruzione di inserimento sql ha richiesto da ~ 300 a ~ 450ms. mentre le statistiche di seguito sono per inline multiple istruzioni di inserimento.

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

Direi che in linea è la strada da percorrere :)


0

È ridicolo quanto male Mysql e MariaDB siano ottimizzati quando si tratta di inserti. Ho provato mysql 5.7 e mariadb 10.3, nessuna vera differenza su quelli.

Ho provato questo su un server con dischi NVME, 70.000 IOPS, throughput seq da 1,1 GB / sec e questo è possibile full duplex (lettura e scrittura).
Il server è anche un server ad alte prestazioni.
Gli ho dato 20 GB di ram.
Il database è completamente vuoto.

La velocità che ricevo era di 5000 inserti al secondo quando si eseguono inserimenti su più file (provato con blocchi di dati da 1 MB fino a 10 MB)

Ora l'indizio:
se aggiungo un altro thread e lo inserisco nelle SAME table improvvisamente ho 2x5000 / sec. Ancora un thread e ho 15000 totali / sec

Considera questo: quando si inseriscono ONE thread significa che è possibile scrivere in sequenza sul disco (con eccezioni agli indici). Quando si utilizzano i thread, si riducono effettivamente le prestazioni possibili perché ora è necessario eseguire molti più accessi casuali. Ma il controllo della realtà mostra che mysql è così mal ottimizzato che i thread aiutano molto.

Le prestazioni reali possibili con un server di questo tipo sono probabilmente milioni al secondo, la CPU è inattiva il disco è inattivo.
La ragione è abbastanza chiara che mariadb come mysql ha ritardi interni.


@Craftables hai bisogno di sviluppo esterno, non può essere fatto all'interno di mysql. Discussioni significa che usi più connessioni al server, dividi la query in più blocchi (ad esempio suddividendolo in parti pari per chiave primaria). Sono riuscito a ottenere fino a 10.000 volte le prestazioni usando questo metodo su tavoli molto grandi. Le query che verrebbero eseguite per 40.000 secondi possono terminare in 2-3 minuti SE usi più thread e il tuo mysql è altamente ottimizzato.
Giovanni,

@John Interessante e potrebbe avere delle belle applicazioni davvero ... ma ... Se dividi la query in più blocchi come gestisci le transazioni? E considera anche il seguente scenario: La tabella x ha una colonna 'parent_id' che si riferisce alla stessa tabella 'id'. Da qualche parte all'interno dei tuoi dati hai INSERISCI IN x ( id, parent_id) VALUES (1, NULL). Una delle prossime serie di valori si collega a quella riga. Se si divide in blocchi e quel set arriva in un altro blocco, potrebbe essere elaborato prima del primo, fallendo l'intero processo. Qualche idea su come gestirlo?
zozo,

@zozo questo è utile per inserimenti di massa e query di massa. Le transazioni rovinerebbero comunque le prestazioni in quanto includono un sacco di buffering dei dati. Ma puoi anche usare le transazioni in inserti o query multi-thread.
Giovanni,

-2

più inserti sono più veloci ma è necessario. un altro thrik sta disabilitando i controlli dei vincoli che rendono temporaneo l'inserimento molto più veloce. Non importa se il tuo tavolo ce l'ha o no. Ad esempio, prova a disabilitare le chiavi esterne e goditi la velocità:

SET FOREIGN_KEY_CHECKS=0;

al di fuori del corso dovresti riaccenderlo dopo gli inserti:

SET FOREIGN_KEY_CHECKS=1;

questo è un modo comune per inserire dati enormi. l'integrità dei dati potrebbe interrompersi, quindi è necessario occuparsene prima di disabilitare i controlli delle chiavi esterne.


1
Non ho idea del perché ppl abbia votato a questo per due motivi: 1. Non ha nulla a che fare con la domanda 2. È una pessima idea (con alcune eccezioni - come il dumping o i cambiamenti di temperatura strutturali -, ma in generale male). I controlli sono lì per un motivo: sono lì per garantire la coerenza dei dati. Rallentano le cose perché si assicurano di non inserire o modificare in altro modo i dati che non si dovrebbero. Prova a ottimizzare le query nel modo giusto; in qualsiasi ambiente aziendale critico ciò significherebbe la morte dell'app poiché, a prescindere da quanto stai attento, le cose falliranno ad un certo punto.
zozo,

1
forse, ma questa opzione è estremamente efficace nell'importazione di tabelle di grandi dimensioni e molto pratica e potrebbe dare ad alcune persone un'idea di come rendere l'inserimento dei dati molto più veloce.
MSS,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.