Risposte:
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.
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.)
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.
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!
Potresti volere :
A seconda di quanto bene il tuo server si ridimensiona (è definitivamente ok con PostgreSQl
, Oracle
e MSSQL
), fai la cosa sopra con più thread e connessioni multiple.
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.
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 :)
È 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.
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?
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.