Warum sollte MySQL Index Kreuzung statt kombinierten Index verwenden?
-
13-10-2019 - |
Frage
Von Zeit zu Zeit treffe ich ein seltsames MySQL Verhalten. Nehmen wir an, ich habe Indizes (Typ, rel, erstellt), (Art), (rel). Die beste Wahl für eine Abfrage wie diese:
SELECT id FROM tbl
WHERE rel = 3 AND type = 3
ORDER BY created;
wäre der Index (type, rel, created)
.
Aber MySQL entscheidet zu schneiden Indizes (type)
und (rel)
, und das führt zu schlechter Performance. Hier ein Beispiel:
mysql> EXPLAIN
-> SELECT id FROM tbl
-> WHERE rel = 3 AND type = 3
-> ORDER BY created\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tbl
type: index_merge
possible_keys: idx_type,idx_rel,idx_rel_type_created
key: idx_type,idx_rel
key_len: 1,2
ref: NULL
rows: 4343
Extra: Using intersect(idx_type,idx_rel); Using where; Using filesort
Und die gleiche Abfrage, aber mit einem Hauch hinzugefügt:
mysql> EXPLAIN
-> SELECT id FROM tbl USE INDEX (idx_type_rel_created)
-> WHERE rel = 3 AND type = 3
-> ORDER BY created\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tbl
type: ref
possible_keys: idx_type_rel_created
key: idx_type_rel_created
key_len: 3
ref: const,const
rows: 8906
Extra: Using where
Ich denke, MySQL einen Ausführungsplan nimmt die geringere Anzahl in der „Zeilen“ Spalte des Befehls EXPLAIN enthält. Von diesem Standpunkt aus betrachtet, Index Kreuzung mit 4343 Zeilen sehen wirklich besser als meinen kombinierten Index mit 8906 Zeilen verwenden. Also, vielleicht ist das Problem in diesen Zahlen?
mysql> SELECT COUNT(*) FROM tbl WHERE type=3 AND rel=3;
+----------+
| COUNT(*) |
+----------+
| 3056 |
+----------+
Aus dieser kann ich feststellen, dass MySQL bei der Berechnung ungefähre Anzahl der Zeilen für die kombinierten Index verwechselt wird.
Also, was kann ich hier tun, um MySQL den richtigen Ausführungsplan nehmen?
Ich kann Hinweise verwenden Optimierer, weil ich Django ORM zu haften haben Die einzige Lösung, die ich gefunden noch ist dieses Ein-Feld-Indizes zu entfernen.
MySQL-Version ist 5.1.49.
Die Tabellenstruktur ist:
CREATE TABLE tbl (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` tinyint(1) NOT NULL,
`rel` smallint(2) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_type` (`type`),
KEY `idx_rel` (`rel`),
KEY `idx_type_rel_created` (`type`,`rel`,`created`)
) ENGINE=MyISAM;
Lösung
Es ist schwer, genau zu sagen, warum MySQL
wählt über den Index-Scan index_merge_intersection
, aber Sie sollten beachten, dass mit dem zusammengesetzten Indizes, Statistiken bis zu der angegebenen Spalte werden für die zusammengesetzte Indizes gespeichert.
Der Wert von information_schema.statistics.cardinality
für die Spalte type
des Composite-Index wird die Mächtigkeit von (rel, type)
zeigen, sich nicht type
.
Wenn es eine Korrelation zwischen rel
und type
ist, dann Mächtigkeit (rel, type)
geringer sein als Produkt der Mächtigkeiten von rel
und type
getrennt von dem Indizes genommen auf Spalten entsprechen.
Das ist, warum die Anzahl der Zeilen falsch berechnet wird (ein Schnittpunkt kann nicht größer sein in der Größe als eine Vereinigung).
Sie können index_merge_intersection
verbieten, indem es weg in @@optimizer_switch
Einstellung:
SET optimizer_switch = 'index_merge_intersection=off'
Andere Tipps
Eine andere Sache ist erwähnenswert: Sie wäre nicht das Problem, wenn Sie den Index nach Typ nur gelöscht. der Index ist nicht erforderlich, da sie einen Teil des zusammengesetzten Index dupliziert.