2013-10-09 12 views
9

Sto tentando di memorizzare un risultato di query in una tabella temporanea per un'ulteriore elaborazione.Perché MySQL 'inserisce in ... selezionare ...' molto più lento di una selezione da solo?

create temporary table tmpTest 
(
    a FLOAT, 
    b FLOAT, 
    c FLOAT 
) 
engine = memory; 

insert into tmpTest 
(
    select a,b,c from someTable 
    where ... 
); 

Ma per qualche motivo l'inserto richiede fino a un minuto, mentre la subselect prende solo pochi secondi. Perché impiegare molto più tempo a scrivere i dati su una tabella temporanea invece di stamparli sull'output del mio strumento di gestione SQL ???

UPDATE mio Setup: MySQL 7.3.2 Cluster con 8 Debian Linux NDB nodi di dati 1 SQL Node (Windows Server 2012)

La tabella sto correndo il select su è un ndb tavolo.

ho cercato di scoprire, se il piano di esecuzione può essere diverso quando si usa 'inserire nel ..', ma hanno lo stesso aspetto: (scusate per la formattazione, StackOverflow non ha tabelle)

 
id select_type  table  type possible_keys key  key_len ref     rows  Extra 
1 PRIMARY   <subquery3> ALL  \N    \N  \N  \N     \N   \N 
1 PRIMARY   foo   ref  PRIMARY   PRIMARY 3  <subquery3>.fooId 9747434  Using where 
2 SUBQUERY  someTable range PRIMARY   PRIMARY 3  \N     136933000 Using where with pushed condition; Using MRR; Using temporary; Using filesort 
3 MATERIALIZED tmpBar  ALL  \N    \N  \N  \N     1000  \N 

CREATE TABLE ... SELECT è lento anche. 47 secondi contro 5 secondi senza inserimento/creazione tabella.

+1

Dovresti essere più specifico e fornire alcuni dati che stai scrivendo. Anche la sintassi 'INSERT .. SELECT' è diversa. Il tuo campione provocherà un errore. –

+0

buona domanda. Non so davvero come mysql riserva 'memoria' per se stesso. Se passa attraverso l'API del sistema operativo, potrebbe semplicemente * richiede * per la memoria, che verrà quindi utilizzata come * ram * o * harddrive * space, in base allo stato del sistema. Vedi la gestione dei byte virtuali di Windows. – Sebas

+0

Hai provato una query di tipo 'CREATE TABLE ... SELECT' per il confronto? http://dev.mysql.com/doc/refman/5.0/en/create-table-select.html – xiankai

risposta

0

Il motivo ha a che fare con il modo in cui il computer legge e scrive e il modo in cui funziona un file temporaneo. Select sta leggendo i dati che si trovano in un file indicizzato sul disco rigido mentre insert sta usando un file temporaneo e sta scrivendo su quel file. È necessaria più RAM e farlo è più difficile. Per quanto riguarda il motivo per cui ci vuole un minuto, non sono sicuro esattamente, ma penso che il codice potrebbe essere un po 'scorretto e quindi contribuirebbe.

+0

La mia tabella temporanea ha il suo motore impostato esplicitamente su "memoria". Questo non significa che dovrebbe rimanere nella RAM? La mia macchina ha 32 GB di RAM di cui 39 Il mio tavolo temporaneo ha solo 1000 righe ... – Ben

+0

@ben wehich os? – Sebas

+0

@Sebas vedi il mio aggiornamento – Ben

1

Ho scritto un commento sopra, poi ci siamo imbattuti in questo come una soluzione.

Questo comporterà ciò che si vuole fare.

SELECT * FROM aTable INTO OUTFILE '/tmp/atable.txt'; 
LOAD DATA INFILE '/tmp/atable.txt' INTO TABLE anotherTable; 

Nota che facendo questo significa che la gestione delle tabelle/tmp in qualche modo. Se si tenta di selezionare i dati in un file OUTFILE già esistente, si ottiene un errore. Quindi è necessario generare nomi di file temporanei unici. E poi eseguire un cron job di qualche tipo per pulirli.

Immagino che INFILE e OUTFILE si comportino diversamente. Se qualcuno può far luce su quello che sta succedendo qui per spiegare il comportamento di mysql, lo apprezzerei.

D

Ecco un modo migliore di utilizzare INFILE/OUTFILE.

IMPOSTAZIONE LIVELLO DI ISOLAMENTO LIVELLO DI MISURA COMMESSO; INSERT INTO Atable SELECT ... FROM ...

Ecco un post relativo a leggere:

How to improve INSERT INTO ... SELECT locking behavior

1

ho esperienze lo stesso problema e stavo giocando intorno con sottoquery che in realtà risolto. Se la selezione ha un'enorme quantità di righe, richiede molto tempo per inserire i dati. Esempio:

INSERT INTO b2b_customers (b2b_name, b2b_address, b2b_language) 
SELECT customer_name, customer_address, customer_language 
FROM customers 
WHERE customer_name LIKE "%john%" 
ORDER BY customer_created_date DESC 
LIMIT 1 

utilizzando LIMIT in combinazione per inserire i dati, non è una buona opzione. Quindi è possibile utilizzare 2 query separate per ottenere dati e inserimento, oppure è possibile utilizzare una sottoquery. Esempio:

INSERT INTO b2b_customers (b2b_name, b2b_address, b2b_language) 
SELECT * FROM (
SELECT customer_name, customer_address, customer_language 
FROM customers 
WHERE customer_name LIKE "%john%" 
ORDER BY customer_created_date DESC 
LIMIT 1 
) sub1 

che sarebbe una soluzione veloce senza cambiare lo script.

Quindi non sono sicuro del motivo per cui sono stati necessari 0,01 secondi per eseguire la subquery e 60 secondi per eseguire l'inserto. Ottengo 1000+ risultati senza il limite. Nel mio caso la subquery ha migliorato le prestazioni da 60 secondi a 0,01 secondi.

+0

Non sei sicuro del motivo per cui ci vuole un millisecondo per ** leggere ** i dati un 60 secondi a ** scrivi ** 1000+ record? Forse perché leggere è più veloce della scrittura? – Mjh

+0

sì, è vero, ma sto scrivendo solo 1 riga durante l'utilizzo di LIMIT 1. – joevette

+0

'WHERE customer_name LIKE"% john% "' - questa è una scansione completa della tabella. Una volta ogni singolo record viene scansionato e ridotto per recuperare ciò che contiene 'john'. Questo richiede un po 'di tempo. Quindi scrivi 1 record. È inefficiente – Mjh