2015-04-19 6 views
5

Ho una tabella MySQL con 100k righe che cattura alcuni log del server creati come:Aggiornamento-join in MySQL estremamente lento rispetto alla query di selezione, senza indici

CREATE TABLE `logs` ( 
    `id` INT NOT NULL AUTO_INCREMENT, 
    `ip` VARCHAR(16) NULL, 
    `date` DATETIME NULL, 
    `session_time` SMALLINT UNSIGNED NULL, 
    PRIMARY KEY (`id`)); 

Sto cercando di calcolare il tempo di sessione come il differenza di tempo tra righe consecutive dello stesso IP. Sono in grado di raggiungere questo obiettivo con la seguente query di selezione che richiede meno di un secondo:

SELECT * FROM logs AS a 
LEFT JOIN (
    SELECT id, 
     from_unixtime(@diff) AS starttime, 
     date AS endtime, 
     IF(@diff = 0, 0, (unix_timestamp(date) - @diff)/60) AS session_time1, 
     @diff := unix_timestamp(date) 
    FROM logs, 
     (SELECT @diff := 0) AS x 
    ORDER BY ip, logs.date 
) AS b ON 
    a.id = b.id 

Tuttavia, quando cerco di usare la query precedente in un update-iscriverti per aggiornare la sessione di tempo, la seguente query di aggiornamento dura più di 600 secondi:

UPDATE logs AS a 
LEFT JOIN (
    SELECT id, 
     from_unixtime(@diff) AS starttime, 
     date AS endtime, 
     IF(@diff = 0, 0, (unix_timestamp(date) - @diff)/60) AS session_time1, 
     @diff := unix_timestamp(date) 
    FROM logs, 
     (SELECT @diff := 0) AS x 
    ORDER BY ip, logs.date 
) AS b ON 
    a.id = b.id 
SET session_time = session_time1; 

Cosa mi manca?

Grazie!

UPDATE: Ecco la EXPLAIN del select:

+----+-------------+------------+--------+---------------+------+--------+ 
| id | select_type | table | type | possible_keys | key | rows | 
+----+-------------+------------+--------+---------------+------+--------+ 
| 1 | PRIMARY  | a   | ALL | NULL   | NULL | 109029 | 
| 1 | PRIMARY  | <derived2> | ALL | NULL   | NULL | 108680 | 
| 2 | DERIVED  | <derived3> | system | NULL   | NULL | 1  | 
| 2 | DERIVED  | logs  | ALL | NULL   | NULL | 109029 | 
| 3 | DERIVED  | NULL  | NULL | NULL   | NULL | NULL | 
+----+-------------+------------+--------+---------------+------+--------+ 
+1

'WHERE ip = '...''? Sembra che tu stia aggiornando tutte le voci 100k ma quelle che stai selezionando ('LEFT JOIN' = elementi che non sono conformi alle regole di selezione nel primo gruppo). Prova a usare "INNER JOIN"? –

+0

Grazie Alejandro, ma non capisco il tuo commento. Perché il inner join dovrebbe essere migliore? Cosa intendi con la clausola 'where'? – kahlo

+0

Scusa, avrei dovuto spiegarlo meglio. Quando usi 'UPDATE', di solito usi una clausola' WHERE' per filtrare quali dati devono essere aggiornati. Nel tuo caso, come vuoi aggiornare per un indirizzo IP specifico, facendo 'WHERE ip = ''' dovrebbe essere meglio per elaborare solo i dati con quell'IP specifico (sto pensando che la tua query stia elaborando ogni record nella tua tabella , anche se non tutti vengono aggiornati). –

risposta

0

iniziare con session_time essere NULL in tutte le righe. Modificare la query in due modi:

  • Aggiungere WHERE session_time IS NULL alla UPDATE (alla fine)
  • Set session_time a NULL se non si dispone di un tempo di chiusura, altrimenti impostato correttamente.

La prima notte, sarà lenta come ora, ma dopo sarà molto più veloce perché lavorerà solo su alcune delle "nuove" righe.

Modifica

A JOIN esigenze (di solito) una clausola ON. Che ne dici di collegare logs e a con il PRIMARY KEY di logs. Il numero EXPLAIN mostra che è necessario eseguire le combinazioni 109K * 108K di logs e a; dovrebbe essere solo 109K.

Inoltre, rimuovere LEFT a meno che non sia necessario.

+0

Grazie Rick, ho incluso il tuo suggerimento. Tuttavia, questa query contiene già solo le nuove informazioni generate in un giorno (100k righe al giorno). Sono ancora incuriosito da ciò che può causare tale differenza di orario e correggerlo. – kahlo

+0

Se si sta eseguendo 5.6, si prega di postare "EXPLAIN SELECT ..." e "EXPLAIN UPDATE ...". Forse questo mostrerà che l'ottimizzatore sta facendo qualcosa di diverso tra loro. –

+0

Ho appena aggiunto il comando "EXPLAIN SELECT ..." ma non riesco ad aggiungere "EXPLAN UPDATE ..." poiché sto eseguendo la versione 5.5.29 e il precedente 5.6.3 non era permesso. – kahlo

Problemi correlati