Mysql
 sql >> Database >  >> RDS >> Mysql

SELECT con variabili di query che non utilizzano INDEX

Il motivo risiede nell'uso di OR condizioni nel WHERE clausola.

Per illustrare, prova a eseguire di nuovo la query, questa volta solo con id = 5 condizione e ottenere (output EXPLAIN):

+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
| id | select_type | table      | type   | possible_keys      | key     | key_len | ref   | rows | Extra          |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
|  1 | PRIMARY     | <derived2> | system | NULL               | NULL    | NULL    | NULL  |    1 |                |
|  1 | PRIMARY     | tree       | const  | PRIMARY,index_both | PRIMARY | 4       | const |    1 |                |
|  2 | DERIVED     | NULL       | NULL   | NULL               | NULL    | NULL    | NULL  | NULL | No tables used |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+

E ancora, questa volta solo con parent_id = @last_id OR parent_id = 5 condizione e ottenere:

+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
| id | select_type | table      | type   | possible_keys   | key  | key_len | ref  | rows | Extra          |
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
|  1 | PRIMARY     | <derived2> | system | NULL            | NULL | NULL    | NULL |    1 |                |
|  1 | PRIMARY     | tree       | ALL    | index_parent_id | NULL | NULL    | NULL |   10 | Using where    |
|  2 | DERIVED     | NULL       | NULL   | NULL            | NULL | NULL    | NULL | NULL | No tables used |
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+

MySQL non è molto bravo a gestire più indici nella stessa query. Le cose vanno leggermente meglio con le condizioni AND; è più probabile che si veda un index_merge ottimizzazione rispetto a un unione di indici ottimizzazione.

Le cose stanno migliorando con l'avanzare delle versioni, ma ho testato la tua query sulla versione 5.5 , che è l'ultima versione di produzione corrente, e i risultati sono come descrivi.

Per spiegare perché questo è difficile, considera:due diversi indici risponderanno a due diverse condizioni della query. Uno risponderà per id = 5 , l'altro per parent_id = @last_id OR parent_id = 5 (A proposito, nessun problema con OR all'interno di quest'ultimo, poiché entrambi i termini sono gestiti all'interno dello stesso indice).

Non esiste un unico indice che possa rispondere per entrambi, e quindi il FORCE INDEX l'istruzione viene ignorata. Vedi, FORCE INDEX dice che MySQL deve usare un indice su una scansione della tabella. Non implica che debba utilizzare più di un indice su una scansione della tabella.

Quindi MySQL segue le regole della documentazione qui. Ma perché è così complicato? Perché per rispondere utilizzando entrambi gli indici, MySQL deve raccogliere risultati da entrambi, archiviarne uno in un buffer temporaneo mentre gestisce il secondo. Quindi è necessario passare su quel buffer per filtrare le righe identiche (è possibile che alcune righe soddisfino tutte le condizioni). E poi scansionare quel buffer in modo da restituire i risultati.

Ma aspetta, quel buffer di per sé non è indicizzato. Filtrare i duplicati non è un compito ovvio. Quindi MySQL preferisce lavorare sulla tabella originale e fare la scansione lì, ed evitare tutto quel pasticcio.

Ovviamente questo è risolvibile. Gli ingegneri di Oracle potrebbero ancora migliorarlo (di recente hanno lavorato duramente per migliorare i piani di esecuzione delle query), ma non so se questo è nell'attività TODO o se ha una priorità elevata.