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;
War es hilfreich?

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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top