Почему оптимизатор запросов MySQL выбирает вторичный индекс по кластерному первичному индексу? - PullRequest
5 голосов
/ 15 ноября 2011

Почему оптимизатор Mysql выбирает вторичный индекс при выполнении операции «выбор * из поиска» без указания порядка по словам.

Это просто случайность или это негласная оптимизация, предполагаемая с момента добавления вамивторичный индекс важнее первичного ключа.

Я ожидаю, что результаты будут упорядочены по первичному ключу, так как сканирование всех конечных узлов может предоставить все данные, необходимые для ответа на этот запрос.

Для воспроизведения я создаю простую таблицу пар ключ / значение (примечание не auto_increment)

create table lookup (
id int not null,
primary key (id),
name varchar(25),
unique k_name (name)
) engine=innodb;

Вставка некоторых данных в случайном не алфавитном порядке

insert into lookup values(1, "Zebra"),(2, "Aardvark"),(3, "Fish"),(4,"Dog"),(5,"Cat"),(6,"Mouse");

Запрос данных (именно здесь я ожидаю, что данные будут возвращены в порядке первичного ключа)

mysql> select * from lookup;
+----+----------+
| id | name     |
+----+----------+
|  2 | Aardvark |
|  5 | Cat      |
|  4 | Dog      |
|  3 | Fish     |
|  6 | Mouse    |
|  1 | Zebra    |
+----+----------+
6 rows in set (0.00 sec)

Где, как это не так - кажется, что сканирование конечных узлов k_name было выполнено.Показанный здесь

mysql> explain select * from lookup;
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+
| id | select_type | table  | type  | possible_keys | key    | key_len | ref  | rows | Extra       |
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+
|  1 | SIMPLE      | lookup | index | NULL          | k_name | 28      | NULL |    6 | Using index |
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+
1 row in set (0.00 sec)

Для меня это говорит, что Mysql использует k_name в качестве индекса покрытия для возврата данных.Если я опускаю индекс k_name, тогда данные возвращаются в порядке первичного ключа.При добавлении еще одного неиндексированного столбца данные возвращаются в порядке первичного ключа.

Некоторая базовая информация о моей настройке.

mysql> show table status like 'lookup'\G
*************************** 1. row ***************************
           Name: lookup
         Engine: InnoDB
        Version: 10
     Row_format: Compact
           Rows: 6
 Avg_row_length: 2730
    Data_length: 16384
Max_data_length: 0
   Index_length: 16384
      Data_free: 0
 Auto_increment: NULL
    Create_time: 2011-11-15 10:42:35
    Update_time: NULL
     Check_time: NULL
      Collation: latin1_swedish_ci
       Checksum: NULL
 Create_options:
        Comment:
1 row in set (0.00 sec)

 mysql> select version();
 +------------+
 | version()  |
 +------------+
 | 5.5.15-log |
 +------------+
 1 row in set (0.00 sec)

Ответы [ 4 ]

4 голосов
/ 15 ноября 2011

На самом деле кластерный индекс (он же gen_clust_index ) заполняется в порядке, который не имеет никакой рифмы или причины, кроме порядка строк. практически невозможно заказать rowids в порядке id.

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

Вторичный индекс определяет порядок. Однако каждая запись вторичного индекса имеет запись первичного ключа в правильной строке. Также подумайте о сценарии покрытия индекса, который вы упомянули для k_name.

Теперь давайте переключимся на минуту и ​​обсудим ПЕРВИЧНЫЙ КЛЮЧ и k_name:

ВОПРОС : у кого есть дополнительные столбцы, запрошенные вашим исходным запросом, первичным ключом или k_name?

ОТВЕТ : k_name, потому что в нем есть и имя, и идентификатор (id является внутренним, потому что это ПЕРВИЧНЫЙ КЛЮЧ). Покрывающий индекс k_name выполняет запрос лучше, чем первичный ключ.

Теперь, если запрос был SELECT * FROM ORDER BY id, ваш EXPLAIN PLAN должен выглядеть следующим образом:

mysql> explain select * from lookup order by id;
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+
| id | select_type | table  | type  | possible_keys | key     | key_len | ref  | rows | Extra |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+
|  1 | SIMPLE      | lookup | index | NULL          | PRIMARY | 4       | NULL |    6 |       |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+

1 row in set (0.00 sec)

Без указания порядка MySQL Query Optimizer выбирает индекс, который наилучшим образом соответствует вашему запросу. Конечно, k_name имеет несправедливое преимущество, потому что

  • каждый столбец в таблице индексируется индивидуально
  • каждый столбец в таблице - это ключ-кандидат
  • k_name НЕ ВТОРИЧНЫЙ ИНДЕКС , потому что это ключ-кандидат, как и ПЕРВИЧНЫЙ КЛЮЧ.
  • пользовательские кластерные индексы не могут изменять порядок строк после их установки

Вы не можете манипулировать порядком строк вообще. Вот доказательство тому:

mysql> alter table lookup order by name;
Query OK, 6 rows affected, 1 warning (0.23 sec)
Records: 6  Duplicates: 0  Warnings: 1

mysql> show warnings;
+---------+------+-----------------------------------------------------------------------------------+
| Level   | Code | Message                                                                           |
+---------+------+-----------------------------------------------------------------------------------+
| Warning | 1105 | ORDER BY ignored as there is a user-defined clustered index in the table 'lookup' |
+---------+------+-----------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> alter table lookup order by id;
Query OK, 6 rows affected, 1 warning (0.19 sec)
Records: 6  Duplicates: 0  Warnings: 1

mysql> show warnings;
+---------+------+-----------------------------------------------------------------------------------+
| Level   | Code | Message                                                                           |
+---------+------+-----------------------------------------------------------------------------------+
| Warning | 1105 | ORDER BY ignored as there is a user-defined clustered index in the table 'lookup' |
+---------+------+-----------------------------------------------------------------------------------+
1 row in set (0.00 sec)
1 голос
/ 15 ноября 2011

Это потому, что вторичные индексы InnoDB также включают столбец первичного ключа.Поэтому MySQL может извлекать все соответствующие данные непосредственно из вторичного индекса, не касаясь строк данных, и поэтому сохраняет дисковый ввод-вывод.

Ссылки:

1 голос
/ 15 ноября 2011

Что ж, любой индекс одинаково эффективен с точки зрения получения данных для этого запроса, поэтому я предполагаю, что оптимизатор просто выпал с "this will do"

Добавьте еще один уникальный индекс, возможно, так как они все одинаково эффективны, некоторая подпрограмма «FindBestIndex» выпадает вместе с последней прочитанной.

Это не то поведение, которого я ожидал бы, хотя, если бы я заботился о порядке, я бы добавил порядок по идентификатору, и они позволили оптимизатору выбрать первичный ключ вместо двух проходов и сортировки.

0 голосов
/ 17 мая 2013

Я думаю, вы не поняли столбец типа. Тип столбца «индекс» означает полное сканирование индекса. Если это так и столбец «extra» содержит «using index», это означает, что mysql может получить все данные, необходимые для запроса из индекса, и ему не нужно обращаться к фактическим строкам таблицы. Таким образом, здесь механизм вместо перехода к строкам (что обычно является дорогостоящим) использует индекс, содержащий все данные, необходимые для запроса. Вторичные индексы имеют первичный ключ (id, в вашем случае) в качестве данных. То есть, если вы посмотрите ключ во вторичном индексе, вы получите первичные ключи записей таблицы. Поскольку вы только что запросили все значения, достаточно перебрать вторичный индекс, чтобы получить то, что вам нужно.

Если механизм решил перебирать первичный ключ, первичные ключи непосредственно приводят к фактическим строкам таблицы. Mysql пытается избежать такого поведения, потому что оно обычно неэффективно. Это неэффективно, потому что обычно строки содержат больше данных, чем содержится в индексах, и вам, возможно, придется выполнять больше операций ввода-вывода.

http://dev.mysql.com/doc/refman/5.0/en/explain-output.html

...