Почему MySQL использует пересечение индекса вместо комбинированного индекса? - PullRequest
7 голосов
/ 24 декабря 2010

Время от времени я сталкиваюсь со странным поведением MySQL.Предположим, у меня есть индексы (тип, rel, созданы), (тип), (rel).Лучший выбор для такого запроса:

SELECT id FROM tbl
WHERE rel = 3 AND type = 3
ORDER BY created;

- использовать индекс (type, rel, created).Но MySQL решает пересечь индексы (type) и (rel), и это приводит к ухудшению производительности.Вот пример:

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

И тот же запрос, но с добавленной подсказкой:

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

Я думаю, что MySQL берет план выполнения, который содержит меньше числа в «строках»столбец команды EXPLAIN.С этой точки зрения пересечение индекса с 4343 строками выглядит действительно лучше, чем использование моего комбинированного индекса с 8906 строками.Итак, может быть, проблема в этих числах?

mysql> SELECT COUNT(*) FROM tbl WHERE type=3 AND rel=3;
+----------+
| COUNT(*) |
+----------+
|     3056 |
+----------+

Из этого я могу сделать вывод, что MySQL ошибается при вычислении приблизительного числа строк для комбинированного индекса.

Итак, что я могу сделатьздесь, чтобы заставить MySQL выбрать правильный план выполнения?

Я не могу использовать подсказки оптимизатора, потому что мне нужно придерживаться Django ORM. Единственное решение, которое я нашел, - это удалить эти индексы с одним полем.

Версия MySQL 5.1.49.

Структура таблицы:

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;

Ответы [ 2 ]

12 голосов
/ 24 декабря 2010

Трудно точно сказать, почему MySQL выбирает index_merge_intersection для сканирования индекса, но вы должны заметить, что для составных индексов статистика до указанного столбца сохраняется для составных индексов.

Значение information_schema.statistics.cardinality для столбца type составного индекса будет показывать количество элементов (rel, type), а не type.

Если существует корреляция между rel и typeтогда количество элементов (rel, type) будет меньше произведения значений элементов rel и type, взятых отдельно от индексов в соответствующих столбцах.

Поэтому число строк рассчитывается неправильно (пересечение не можетбыть больше по размеру, чем объединение).

Вы можете запретить index_merge_intersection, установив его в @@optimizer_switch:

SET optimizer_switch = 'index_merge_intersection=off'
3 голосов
/ 24 декабря 2010

Стоит упомянуть еще одну проблему: у вас не возникнет проблемы, если вы удалите индекс только по типу.индекс не является обязательным, поскольку он дублирует часть составного индекса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...