2015-07-16 8 views
6

Si utilizza MySQL 5.5.42.MySQL (id> = N AND col2 IS NULL) query inaspettatamente lenta per grande N

Abbiamo una tabella publications contenente circa 150 milioni di righe (circa 140 GB su un SSD).

La tabella include più colonne, di cui due sono di particolare interesse:

  • id è la chiave primaria della tabella ed è di tipo bigint
  • cluster_id è una colonna Null di tipo bigint

Entrambe le colonne hanno il proprio indice (separato).

Facciamo domande del modulo

SELECT * FROM publications 
WHERE id >= 14032924480302800156 AND cluster_id IS NULL 
ORDER BY id 
LIMIT 0, 200; 

Ecco il problema: Maggiore è il valore id (14032924480302800156 nell'esempio di cui sopra), più lenta è la richiesta.

In altre parole, le richieste di basso valore id sono veloci (< 0,1 s), ma maggiore è il valore id, più lenta è la richiesta (fino a minuti).

Tutto va bene se si utilizza un'altra colonna (indicizzata) nella clausola WHERE. Per esempio

SELECT * FROM publications 
WHERE inserted_at >= '2014-06-20 19:30:25' AND cluster_id IS NULL 
ORDER BY inserted_at 
LIMIT 0, 200; 

dove inserted_at è di tipo timestamp.

Edit:

uscita di EXPLAIN quando si utilizza id >= 14032924480302800156:

id | select_type | table  | type | possible_keys  | key  | key_len | ref | rows  | Extra 
---+-------------+--------------+------+--------------------+------------+---------+-------+----------+------------ 
1 | SIMPLE  | publications | ref | PRIMARY,cluster_id | cluster_id | 9  | const | 71647796 | Using where 

uscita di EXPLAIN quando si utilizza inserted_at >= '2014-06-20 19:30:25':

id | select_type | table  | type | possible_keys   | key  | key_len | ref | rows  | Extra 
---+-------------+--------------+------+------------------------+------------+---------+-------+----------+------------ 
1 | SIMPLE  | publications | ref | inserted_at,cluster_id | cluster_id | 9  | const | 71647796 | Using where 
+3

Forse per 'id' più grandi, è più raro che' cluster_id' corrisponda al valore richiesto ('NULL')? In questo caso per 'id' più grandi il database potrebbe dover attraversare più record per selezionare il 200 richiesto con' cluster_id IS NULL'. –

+0

Mostra l'output di [EXPLAIN] (https://dev.mysql.com/doc/refman/5.0/en/explain.html) sia per l'utilizzo di ID piccoli e grandi. –

+0

@SergeRogatch Questa è un'osservazione eccellente, e ci abbiamo pensato, ma trovo difficile credere che ciò spiegherebbe rallentamenti di tre ordini di grandezza. –

risposta

2

C'è una certa congettura in merito a MySQL che utilizza gli indici nell'ordine sbagliato. L'indice PRIMARY sembra essere trattato in modo completamente diverso dagli altri.

In una query con indici di condizione chiave primaria PRIMARY e su cluster_id può essere utilizzato. Per qualche ragione, MySQL ha ignorato l'indice PRIMARY e guarda prima un indice su cluster_id, dove si ha una condizione: dovrebbe essere NULL. Questo ci lascia un enorme insieme non ordinato (NULL s ovunque!) Di righe da filtrare per id.

Con query successiva, tuttavia, è diverso: PRIMARY indice non può essere utilizzato a tutti, quindi MySQL figure cosa utilizzare in modo migliore, apparentemente utilizzando un indice su inserted_at prima senza alcun suggerimento.

Quello che dovrebbe effettivamente fare in prima query è prendere prima l'indice PRIMARY (tell it to do so). Non sono un utente MySQL, tutte le mie supposizioni sono supportate solo dalla mia comprensione delle strutture di dati interne. Non so se è possibile applicare un indice su cluster_id in cima ai risultati, ma la creazione di un indice composito e il confronto delle prestazioni con e senza di esso può fornire indizi sul suo utilizzo.

+0

Grazie, aggiungendo 'USE INDEX (PRIMARY)' risolve il problema. –

+0

Pensi di ricalcolare la statistica delle tabelle? (È un'impresa data la dimensione del database ...) –

+0

Beh, se sei riuscito a fare una stima migliore di quale indice usare rispetto al programma, potrebbe essere inutile provare a farlo diventare decisioni migliori. Hai già funzionato. Tuttavia, capire "perché" può portare a qualche miglioramento in MySQL stesso. –

0

Il problema di prestazioni è molto probabilmente accade perché hai solo separata indici per colonne id e cluster_id, ma non un indice su entrambe queste colonne contemporaneamente.

Forse per ID più grandi, è più raro che cluster_id corrisponda al valore richiesto (NULL)? In questo caso per gli ID di grandi dimensioni, il database potrebbe dover attraversare più record per selezionare il 200 richiesto con cluster_id IS NULL.

Se si disponesse di un singolo indice su entrambe le colonne, il database non avrebbe bisogno di attraversare quel numero di record perché saprebbe quali record soddisfano simultaneamente i 2 criteri di ricerca.

+0

Abbiamo pensato a questo, ma questo non spiega perché le prestazioni siano eccellenti quando si utilizza 'inserted_at' invece di' id' avere un indice su 'inserted_at' e' cluster_id' contemporaneamente). –

+0

Il valore più elevato di 'inserted_at' corrisponde a' id's più alto? A meno che non si stia utilizzando un SSD, un disco rigido rotante può facilmente dare un rallentamento di diversi ordini di magnitudini se si accede casualmente a piccoli blocchi di dati. Poiché i dischi rigidi rotanti possono eseguire 100-200 operazioni di ricerca al secondo (ovvero il numero di rotazioni complete al secondo). –

+0

Sì, i valori più alti di 'inserted_at' corrispondono ai valori di' id' più alti. –

Problemi correlati