Mysql
 sql >> Datenbank >  >> RDS >> Mysql

SELECT mit Abfragevariablen, die keine INDEXe verwenden

Der Grund liegt in der Verwendung von ODER Bedingungen im WO Klausel.

Versuchen Sie zur Veranschaulichung, die Abfrage erneut auszuführen, diesmal nur mit id = 5 Bedingung und erhalte (EXPLAIN-Ausgabe):

+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
| 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 |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+

Und wieder, diesmal nur mit parent_id = @last_id OR parent_id = 5 Bedingung und erhalte:

+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
| 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 ist nicht allzu gut darin, mehrere Indizes in derselben Abfrage zu handhaben. Bei UND-Bedingungen sieht es etwas besser aus; man sieht eher einen index_merge Optimierung als eine index union Optimierung.

Die Dinge verbessern sich mit fortschreitenden Versionen, aber ich habe Ihre Abfrage auf Version 5.5 getestet , das ist die aktuell neueste Produktionsversion, und die Ergebnisse sind so, wie Sie es beschreiben.

Um zu erklären, warum dies schwierig ist, bedenken Sie Folgendes:Zwei verschiedene Indizes antworten auf zwei verschiedene Bedingungen der Abfrage. Man wird für id = 5 antworten , die andere für parent_id = @last_id OR parent_id = 5 (Übrigens kein Problem mit dem ODER innerhalb des letzteren, da beide Begriffe innerhalb desselben Index behandelt werden).

Es gibt keinen einzigen Index, der beide beantworten kann, und daher den FORCE INDEX Anweisung wird ignoriert. Siehe FORCE INDEX sagt, dass MySQL an verwenden muss Index über einen Tabellenscan. Dies bedeutet nicht, dass mehr als ein Index für einen Tabellenscan verwendet werden muss.

MySQL folgt also den Regeln der Dokumentation hier. Aber warum ist das so kompliziert? Denn um mit beiden Indizes zu antworten, muss MySQL Ergebnisse von beiden sammeln, das eine in einem temporären Puffer beiseite legen, während das zweite verwaltet wird. Dann muss er über diesen Puffer gehen, um identische Zeilen herauszufiltern (es ist möglich, dass einige Zeilen alle Bedingungen erfüllen). Und dann diesen Puffer scannen, um die Ergebnisse zurückzugeben.

Aber warten Sie, dieser Puffer selbst ist nicht indiziert. Das Filtern von Duplikaten ist keine offensichtliche Aufgabe. Daher arbeitet MySQL lieber an der Originaltabelle und führt den Scan dort durch und vermeidet diesen ganzen Schlamassel.

Das ist natürlich lösbar. Die Ingenieure von Oracle können dies noch verbessern (in letzter Zeit haben sie hart daran gearbeitet, Abfrageausführungspläne zu verbessern), aber ich weiß nicht, ob dies zur TODO-Aufgabe gehört oder ob es eine hohe Priorität hat.