Creazione di una tabella MySQL da 1.000 milioni di righe


18

Questa domanda viene ripubblicata da Stack Overflow in base a un suggerimento nei commenti, si scusa per la duplicazione.

Domande

Domanda 1: man mano che le dimensioni della tabella del database aumentano, come posso ottimizzare MySQL per aumentare la velocità della chiamata LOAD DATA INFILE?

Domanda 2: utilizzare un cluster di computer per caricare diversi file CSV, migliorare le prestazioni o ucciderlo? (questo è il mio compito di benchmarking per domani usando i dati di carico e gli inserti di massa)

Obbiettivo

Stiamo provando diverse combinazioni di rilevatori di funzionalità e parametri di clustering per la ricerca di immagini, di conseguenza dobbiamo essere in grado di costruire e grandi database in modo tempestivo.

Informazioni sulla macchina

La macchina ha 256 gig di ram e ci sono altre 2 macchine disponibili con la stessa quantità di ram se c'è un modo per migliorare i tempi di creazione distribuendo il database?

Schema della tabella

sembra lo schema della tabella

+---------------+------------------+------+-----+---------+----------------+
| Field         | Type             | Null | Key | Default | Extra          |
+---------------+------------------+------+-----+---------+----------------+
| match_index   | int(10) unsigned | NO   | PRI | NULL    |                |
| cluster_index | int(10) unsigned | NO   | PRI | NULL    |                |
| id            | int(11)          | NO   | PRI | NULL    | auto_increment |
| tfidf         | float            | NO   |     | 0       |                |
+---------------+------------------+------+-----+---------+----------------+

creato con

CREATE TABLE test 
(
  match_index INT UNSIGNED NOT NULL,
  cluster_index INT UNSIGNED NOT NULL, 
  id INT NOT NULL AUTO_INCREMENT,
  tfidf FLOAT NOT NULL DEFAULT 0,
  UNIQUE KEY (id),
  PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;

Benchmarking finora

Il primo passo è stato quello di confrontare inserimenti di massa vs caricamento da un file binario in una tabella vuota.

It took:  0:09:12.394571  to do  4,000  inserts with 5,000 rows per insert
It took: 0:03:11.368320 seconds to load 20,000,000 rows from a csv file

Data la differenza di prestazioni che ho seguito con il caricamento dei dati da un file csv binario, per prima cosa ho caricato file binari contenenti file da 100K, 1M, 20M, 200M usando la chiamata in basso.

LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test;

Ho eliminato il caricamento del file binario di 200 m di file (file csv di ~ 3 GB) dopo 2 ore.

Quindi ho eseguito uno script per creare la tabella e inserire diversi numeri di righe da un file binario, quindi rilasciare la tabella, vedere il grafico seguente.

inserisci qui la descrizione dell'immagine

Ci sono voluti circa 7 secondi per inserire 1 milione di righe dal file binario. Successivamente ho deciso di eseguire il benchmark inserendo 1M righe alla volta per vedere se ci sarebbe stato un collo di bottiglia in una dimensione del database particolare. Una volta che il database ha raggiunto circa 59 milioni di righe, il tempo medio di inserimento è sceso a circa 5.000 / secondo

inserisci qui la descrizione dell'immagine

L'impostazione del key_buffer_size globale = 4294967296 ha migliorato leggermente le velocità per l'inserimento di file binari più piccoli. Il grafico seguente mostra le velocità per diversi numeri di righe

inserisci qui la descrizione dell'immagine

Tuttavia, per l'inserimento di righe 1M non ha migliorato le prestazioni.

righe: 1.000.000 di tempo: 0: 04: 13.761428 inserti / sec: 3.940

vs per un database vuoto

righe: 1.000.000 di tempo: 0: 00: 6.339295 inserti / sec: 315.492

Aggiornare

Esecuzione dei dati di caricamento utilizzando la sequenza seguente rispetto al solo utilizzo del comando load data

SET autocommit=0;
SET foreign_key_checks=0;
SET unique_checks=0;
LOAD DATA INFILE '/mnt/imagesearch/tests/eggs.csv' INTO TABLE test_ClusterMatches;
SET foreign_key_checks=1;
SET unique_checks=1;
COMMIT;
inserisci qui la descrizione dell'immagine

Quindi questo sembra abbastanza promettente in termini di dimensioni del database che viene generato ma le altre impostazioni non sembrano influenzare le prestazioni della chiamata di caricamento dei dati di caricamento.

Ho quindi provato a caricare più file da macchine diverse ma il comando di caricamento dei dati di caricamento blocca la tabella, a causa delle grandi dimensioni dei file che causano il timeout delle altre macchine con

ERROR 1205 (HY000) at line 1: Lock wait timeout exceeded; try restarting transaction

Aumentando il numero di righe nel file binario

rows:  10,000,000  seconds rows:  0:01:36.545094  inserts/sec:  103578.541236
rows:  20,000,000  seconds rows:  0:03:14.230782  inserts/sec:  102970.29026
rows:  30,000,000  seconds rows:  0:05:07.792266  inserts/sec:  97468.3359978
rows:  40,000,000  seconds rows:  0:06:53.465898  inserts/sec:  96743.1659866
rows:  50,000,000  seconds rows:  0:08:48.721011  inserts/sec:  94567.8324859
rows:  60,000,000  seconds rows:  0:10:32.888930  inserts/sec:  94803.3646283

Soluzione: pre-calcolare l'id al di fuori di MySQL invece di utilizzare l'incremento automatico

Costruire il tavolo con

CREATE TABLE test (
  match_index INT UNSIGNED NOT NULL,
  cluster_index INT UNSIGNED NOT NULL, 
  id INT NOT NULL ,
  tfidf FLOAT NOT NULL DEFAULT 0,
  PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;

con l'SQL

LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';"

inserisci qui la descrizione dell'immagine

Ottenere lo script per pre-calcolare gli indici sembra aver rimosso l'hit di prestazioni man mano che il database aumenta di dimensioni.

Aggiornamento 2: utilizzo delle tabelle di memoria

Circa 3 volte più veloce, senza tenere conto del costo dello spostamento di una tabella in memoria su una tabella basata su disco.

rows:  0  seconds rows:  0:00:26.661321  inserts/sec:  375075.18851
rows:  10000000  time:  0:00:32.765095  inserts/sec:  305202.83857
rows:  20000000  time:  0:00:38.937946  inserts/sec:  256818.888187
rows:  30000000  time:  0:00:35.170084  inserts/sec:  284332.559456
rows:  40000000  time:  0:00:33.371274  inserts/sec:  299658.922222
rows:  50000000  time:  0:00:39.396904  inserts/sec:  253827.051994
rows:  60000000  time:  0:00:37.719409  inserts/sec:  265115.500617
rows:  70000000  time:  0:00:32.993904  inserts/sec:  303086.291334
rows:  80000000  time:  0:00:33.818471  inserts/sec:  295696.396209
rows:  90000000  time:  0:00:33.534934  inserts/sec:  298196.501594

caricando i dati in una tabella basata sulla memoria e quindi copiandoli in una tabella basata sul disco in blocchi, si è avuto un sovraccarico di 10 min 59,71 secondi per copiare 107.356.741 righe con la query

insert into test Select * from test2;

che richiede circa 15 minuti per caricare 100 milioni di righe, che è approssimativamente lo stesso che inserirlo direttamente in una tabella basata su disco.


1
Penso che cambiare la chiave primaria in semplicemente iddovrebbe essere più veloce. (Anche se penso che non lo stia cercando)
DavidEG,

Ciao David, grazie per il commento, sfortunatamente senza la chiave le domande che dobbiamo fare non sono abbastanza veloci (la logica dietro la selezione della chiave primaria è delineata in questo post stackoverflow.com/questions/4282526/mysql-group-by- ottimizzazione )
Ben

1
Questo è solo per il test? Potresti voler guardare il motore di MEMORIA di MySQL: dev.mysql.com/doc/refman/5.0/en/memory-storage-engine.html Se hai intenzione di implementarlo come architettura, sono curioso di sapere come intendi recuperare da guasti, sembra qualcosa che sarebbe meglio gestito da MapReduce / Hadoop.
polinomio

Ciao polinomio, grazie per il suggerimento, al momento stiamo solo testando diversi rilevatori di funzionalità su scale diverse, una volta generato il database non cambierà molto (nelle specifiche attuali comunque)
Ben

Risposte:


4

Bella domanda - ben spiegata.

come posso sintonizzare MySQL per aumentare la velocità della chiamata LOAD DATA INFILE?

Hai già un'impostazione elevata (ish) per il buffer delle chiavi - ma è abbastanza? Suppongo che questa sia un'installazione a 64 bit (se non la prima cosa che devi fare è aggiornare) e non funziona su MSNT. Dai un'occhiata all'output di mysqltuner.pl dopo aver eseguito alcuni test.

Al fine di utilizzare la cache in modo ottimale, è possibile trovare vantaggi nel raggruppamento / pre-ordinamento dei dati di input (le versioni più recenti del comando 'sort' hanno molte funzionalità per l'ordinamento di set di dati di grandi dimensioni). Inoltre, se generi i numeri ID al di fuori di MySQL, potrebbe essere più efficiente.

utilizzerebbe un cluster di computer per caricare diversi file CSV

Supponendo (di nuovo) che si desidera che il set di output si comporti come una singola tabella, gli unici vantaggi che si ottengono sono la distribuzione del lavoro di ordinamento e la generazione di ID, per i quali non sono necessari più database. OTOH usando un cluster di database, otterrai problemi con la contesa (che non dovresti vedere se non come problemi di prestazioni).

Se riesci a dividere i dati e gestire i set di dati risultanti in modo indipendente, allora sì, otterrai vantaggi in termini di prestazioni, ma ciò non annulla la necessità di ottimizzare ciascun nodo.

Verifica di avere almeno 4 Gb per sort_buffer_size.

Oltre a ciò, il fattore limitante in termini di prestazioni riguarda tutto l'I / O del disco. Esistono molti modi per risolvere questo problema, ma probabilmente dovresti prendere in considerazione un set speculare di set di dati con striping su SSD per prestazioni ottimali.


1
  • Considera il tuo fattore limitante. È quasi certamente l'elaborazione della CPU a thread singolo.
  • L'hai già determinato load data... è più veloce di insert, quindi usalo.
  • Hai già determinato che i file di grandi dimensioni (per numero di riga) rallentano molto; vuoi spezzarli in pezzi.
  • Utilizzando chiavi primarie non sovrapposte, mettere in coda almeno N * set di CPU, utilizzando non più di un milione di righe ... probabilmente meno (benchmark).
  • Utilizzare blocchi sequenziali di chiavi primarie in ciascun file.

Se vuoi essere davvero spiffy, puoi creare un programma multi-thread per alimentare un singolo file in una raccolta di pipe denominate e gestire le istanze di inserimento.

In sintesi, non si sintonizza MySQL per questo tanto quanto si sintonizza il carico di lavoro su MySQL.


-1

Non ricordo esattamente il syntacx ma se è inno db è possibile disattivare il controllo delle chiavi esterne.

Inoltre puoi creare l'indice dopo l'importazione, può essere davvero un guadagno in termini di prestazioni.


Il differimento della ricostruzione dell'indice migliora solo le prestazioni in cui il numero di righe già nella tabella è significativamente inferiore al numero di righe che si stanno aggiungendo.
symcbean,
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.