Почему MySQL не использует индекс для сравнения больше? - PullRequest
8 голосов
/ 14 января 2011

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

Вот таблица:


CREATE TABLE userapplication (
  application_id int(11) NOT NULL auto_increment,
  userid int(11) NOT NULL default '0',
  accountid int(11) NOT NULL default '0',
  resume_id int(11) NOT NULL default '0',
  coverletter_id int(11) NOT NULL default '0',
  user_email varchar(100) NOT NULL default '',
  account_name varchar(200) NOT NULL default '',
  resume_name varchar(255) NOT NULL default '',
  resume_modified datetime NOT NULL default '0000-00-00 00:00:00',
  cover_name varchar(255) NOT NULL default '',
  cover_modified datetime NOT NULL default '0000-00-00 00:00:00',
  application_status tinyint(4) NOT NULL default '0',
  application_created datetime NOT NULL default '0000-00-00 00:00:00',
  application_modified timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  publishid int(11) NOT NULL default '0',
  application_visible int(11) default '1',
  PRIMARY KEY  (application_id),
  KEY publishid (publishid),
  KEY application_status (application_status),
  KEY userid (userid),
  KEY accountid (accountid),
  KEY application_created (application_created),
  KEY resume_id (resume_id),
  KEY coverletter_id (coverletter_id),
 ) ENGINE=MyISAM ;

Этот простой запрос, кажется, выполняет полное сканирование таблицы:

SELECT * FROM userapplication WHERE application_id > 1025;

Это вывод EXPLAIN:

+----+-------------+-------------------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table             | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-------------------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | userapplication | ALL  | PRIMARY       | NULL | NULL    | NULL | 784422 | Using where |
+----+-------------+-------------------+------+---------------+------+---------+------+--------+-------------+`

Есть идеи, как помешать этому простому запросу выполнить полное сканирование таблицы?Или мне не повезло?

Ответы [ 5 ]

13 голосов
/ 14 января 2011

MyISAM таблицы не кластеризованы, индекс PRIMARY KEY является вторичным индексом и требует дополнительного просмотра таблицы для получения других значений.

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

Чтобы предотвратить сканирование таблицы, можно добавить подсказку:

SELECT  *
FROM    userapplication FORCE INDEX (PRIMARY)
WHERE   application_id > 1025

, хотя это не обязательно будет более эффективным.

12 голосов
/ 14 января 2011

Возможно, вам лучше позволить MySql определиться с планом запроса.Существует высокая вероятность, что сканирование индекса будет менее эффективным, чем полное сканирование таблицы.

Для этой таблицы на диске имеется две структуры данных

  1. Сама таблица;и
  2. Индекс B-дерева первичного ключа.

При запуске запроса оптимизатор имеет два варианта доступа к данным:

SELECT * FROM userapplication WHERE application_id > 1025;

Использование индекса

  1. Сканирование индекса B-дерева, чтобы найти адрес всех строк, где application_id > 1025
  2. Читатьсоответствующие страницы таблицы, чтобы получить данные для этих строк.

Не использовать индекс

Сканирование всей таблицы и выбор соответствующих записей.

Выбор оптимальной стратегии

Задача оптимизатора запросов - выбрать наиболее эффективную стратегию получения нужных данных.Если есть много строк с application_id > 1025, тогда индекс может быть менее эффективным.Например, если 90% записей имеют application_id > 1025, тогда оптимизатору запросов потребуется отсканировать около 90% конечных узлов индекса b-дерева, а затем прочитать как минимум 90% таблицы, чтобы получить фактическуюданные;это потребовало бы считывания большего количества данных с диска, чем просто сканирование таблицы.

1 голос
/ 14 января 2011

Mysql определенно считает, что полное сканирование таблицы дешевле, чем использование индекса; однако вы можете принудительно использовать ваш первичный ключ в качестве предпочтительного индекса с:

mysql> EXPLAIN SELECT * FROM userapplication FORCE INDEX (PRIMARY) WHERE application_id > 10;

+----+-------------+-----------------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table           | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-----------------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | userapplication | range | PRIMARY       | PRIMARY | 4       | NULL |   24 | Using where |
+----+-------------+-----------------+-------+---------------+---------+---------+------+------+-------------+


Обратите внимание, что при использовании «ИСПОЛЬЗОВАТЬ ИНДЕКС» вместо «FORCE INDEX» только для подсказки mysql в используемом индексе, mysql по-прежнему предпочитает полное сканирование таблицы:

mysql> EXPLAIN SELECT * FROM userapplication USE INDEX (PRIMARY) WHERE application_id > 10;
+----+-------------+-----------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table           | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-----------------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | userapplication | ALL  | PRIMARY       | NULL | NULL    | NULL |   34 | Using where |
+----+-------------+-----------------+------+---------------+------+---------+------+------+-------------+

0 голосов
/ 14 января 2011

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

0 голосов
/ 14 января 2011

Это должен быть случай простого набора:

SELECT * FROM userapplication WHERE application_id > 1025;

Как указано на этой ссылке . Согласно этому руководству, оно должно работать, когда application_id является числовым значением, для нечисловых значений вы должны набрать:

SELECT * FROM userapplication WHERE application_id > '1025';

Не думаю, что с вашим SELECT что-то не так, может быть, это проблема конфигурации таблицы?

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