Ottimizzazione della query di aggregazione MySQL
-
03-07-2019 - |
Domanda
Ho una tabella molto grande (~ 100 milioni di record) in MySQL che contiene informazioni sui file. Una delle informazioni è la data modificata di ciascun file.
Devo scrivere una query che conterà il numero di file che rientrano negli intervalli di date specificati. Per fare ciò ho creato una piccola tabella che specifica questi intervalli (tutti in giorni) e si presenta così:
DateRanges
range_id range_name range_start range_end
1 0-90 0 90
2 91-180 91 180
3 181-365 181 365
4 366-1095 366 1095
5 1096+ 1096 999999999
E ha scritto una query simile a questa:
SELECT r.range_name, sum(IF((DATEDIFF(CURDATE(),t.file_last_access) > r.range_start and DATEDIFF(CURDATE(),t.file_last_access) < r.range_end),1,0)) as FileCount
FROM `DateRanges` r, `HugeFileTable` t
GROUP BY r.range_name
Tuttavia, abbastanza prevedibilmente, l'esecuzione di questa query richiede un'eternità. Penso che sia perché sto chiedendo a MySQL di passare attraverso la HugeFileTable 5 volte, ogni volta che eseguo il calcolo DATEDIFF () su ciascun file.
Quello che voglio fare invece è passare attraverso il record HugeFileTable per record solo una volta, e per ogni file incrementare il conteggio nel totale corrente range_name appropriato. Non riesco a capire come farlo ....
Qualcuno può dare una mano con questo?
Grazie.
EDIT : versione MySQL: 5.0.45, le tabelle sono MyISAM
EDIT2 : ecco la descrizione richiesta nei commenti
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE r ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
1 SIMPLE t ALL NULL NULL NULL NULL 96506321
Soluzione
Innanzitutto, crea un indice su HugeFileTable.file_last_access
.
Quindi prova la seguente query:
SELECT r.range_name, COUNT(t.file_last_access) as FileCount
FROM `DateRanges` r
JOIN `HugeFileTable` t
ON (t.file_last_access BETWEEN
CURDATE() + INTERVAL r.range_start DAY AND
CURDATE() + INTERVAL r.range_end DAY)
GROUP BY r.range_name;
Ecco il EXPLAIN
piano che ho ottenuto quando ho provato questa query su MySQL 5.0.75 (modificato per brevità):
+-------+-------+------------------+----------------------------------------------+
| table | type | key | Extra |
+-------+-------+------------------+----------------------------------------------+
| t | index | file_last_access | Using index; Using temporary; Using filesort |
| r | ALL | NULL | Using where |
+-------+-------+------------------+----------------------------------------------+
Non funzionerà ancora molto bene. Utilizzando GROUP BY
, la query comporta una tabella temporanea, che può essere costosa. Non puoi fare molto al riguardo.
Ma almeno questa query elimina il prodotto cartesiano che avevi nella tua query originale.
aggiornamento: Ecco un'altra query che utilizza una sottoquery correlata ma ho eliminato il <=>.
SELECT r.range_name,
(SELECT COUNT(*)
FROM `HugeFileTable` t
WHERE t.file_last_access BETWEEN
CURDATE() - INTERVAL r.range_end DAY AND
CURDATE() - INTERVAL r.range_start DAY
) as FileCount
FROM `DateRanges` r;
Il piano <=> non mostra alcuna tabella temporanea o fileort (almeno con la quantità banale di righe che ho nelle mie tabelle di test):
+----+--------------------+-------+-------+------------------+--------------------------+
| id | select_type | table | type | key | Extra |
+----+--------------------+-------+-------+------------------+--------------------------+
| 1 | PRIMARY | r | ALL | NULL | |
| 2 | DEPENDENT SUBQUERY | t | index | file_last_access | Using where; Using index |
+----+--------------------+-------+-------+------------------+--------------------------+
Prova questa query sul tuo set di dati e verifica se funziona meglio.
Altri suggerimenti
Bene, inizia assicurandoti che file_last_access
sia un indice per la tabella HugeFileTable
.
Non sono sicuro che sia possibile \ meglio, ma prova prima a calcolare i limiti di date (file dalla data A alla data B ), quindi usa alcuni interroga con > = e < =. Almeno teoricamente migliorerà le prestazioni.
Il confronto sarebbe qualcosa del tipo:
t.file_last_access >= StartDate AND t.file_last_access <= EndDate
Puoi ottenere un piccolo miglioramento rimuovendo CURDATE () e inserendo una data nella query poiché eseguirà questa funzione per ogni riga due volte nel tuo SQL.