2015-11-23 1 views
3

Начиная с версии 5.6 MySQL очень простой, хотя и длинный запрос занимает несколько порядков дольше, чем в 5.4.MySQL 5.6 long ГДЕ В запросе очень медленно

Схема: три таблицы, одна с элементами, одна с категориями и таблица M: N. Создание заявления:

CREATE TABLE element (
    id int(11) NOT NULL AUTO_INCREMENT, 
    name varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 
    PRIMARY KEY (id) 
) ENGINE=InnoDB AUTO_INCREMENT=4257455 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

CREATE TABLE category (
    id int(11) NOT NULL AUTO_INCREMENT, 
    name varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    PRIMARY KEY (id) 
) ENGINE=InnoDB AUTO_INCREMENT=76 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

CREATE TABLE elements_categories (
    id int(11) NOT NULL AUTO_INCREMENT, 
    element_id int(11) NOT NULL, 
    category_id int(11) NOT NULL, 
    PRIMARY KEY (id), 
    UNIQUE KEY element_id (element_id,category_id), 
    KEY elements_categories_element_id (element_id), 
    KEY elements_categories_category_id (category_id), 
    CONSTRAINT D7d489b06a407a0c1c70f108712c815e FOREIGN KEY (category_id) REFERENCES category (id), 
    CONSTRAINT co_element_id_57f4f2ec0db9441c_fk_element_id FOREIGN KEY (element_id) REFERENCES element (id) 
) ENGINE=InnoDB AUTO_INCREMENT=88131737 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

Запрос:

SELECT elements_categories.element_id, category.id, category.name 
FROM category 
    INNER JOIN elements_categories 
     ON category.id = elements_categories.category_id 
WHERE elements_categories.element_id IN (1, 2, 3, ...) 

Таким образом, элемент таблицы не даже играть определенную роль в этом запросе, я уже получил кучу идентификаторов из с с предыдущим запросом. (Отказ от ответственности: я использую ORM, а также вложение первого запроса не ускорял работу.) Число значений в предложении IN может стать очень большим, в моем примере 14240. Это не проблема, занимает десятую часть второй или около того. Это план выполнения:

| id | select_type | table    | type | possible_keys                | key  | key_len | ref        | rows | Extra     | 
+----+-------------+---------------------+--------+---------------------------------------------------------------------------+------------+---------+---------------------------------+-------+--------------------------+ 
| 1 | SIMPLE  | elements_categories | range | element_id,elements_categories_element_id,elements_categories.category_id | element_id | 4  | NULL       | 42720 | Using where; Using index | 
| 1 | SIMPLE  | category   | eq_ref | PRIMARY                 | PRIMARY | 4  | elements_categories.category_id |  1 | NULL      | 

Когда я добавляю один более элемент, время выполнения взрывается до 60 секунд плюс время получения 200 секунд. План выполнения также изменяется на это:

| id | select_type | table    | type | possible_keys                | key        | key_len | ref   | rows | Extra  | 
+----+-------------+---------------------+------+---------------------------------------------------------------------------+---------------------------------+---------+-------------+------+-------------+ 
| 1 | SIMPLE  | category   | ALL | PRIMARY                 | NULL       | NULL | NULL  | 75 | NULL  | 
| 1 | SIMPLE  | elements_categories | ref | element_id,elements_categories_element_id,elements_categories_category_id | elements_categories_category_id | 4  | category.id | 760 | Using where | 

Диапазон и eq_ref поиски обменены на ВСЕ и реф, порядок таблиц включен, не используя elements_categories.category_id, как исх хотя внешний ключ между этими двумя таблицами. Я не понимаю, почему этот план изменился именно так.

Имеются 75 категорий и 4 300 000 элементов и 1600 000 заданий.

Я предполагаю, что я превысил ограничение по размеру здесь, но не могу понять, какой из них. Кроме того, я ничего не менял из установки MySQL 5.5, которая все время придерживалась прежнего плана выполнения.

+0

1-й план запроса не имеет смысла для отправленного вами запроса. Это означает, что предложение where - 'elements_categories.element_id IN (1, 2, 3, ...)' или что есть дополнительные условия/join. Вы уверены, что это 'category.element_id IN ...'? – Vatev

+0

Очень замечательно, спасибо. Ваше первое предположение верно, я изменил запрос соответствующим образом. –

+0

Можно ли «Показать таблицу создания» для двух таблиц? Меня интересуют индексы. Также может быть интересным указание фактического размера таблиц. –

ответ

2

Есть несколько способов обмануть оптимизатор использовать правильный план:

  1. Добавить в index hint: ... JOIN elements_categories FORCE INDEX (element_id)...
  2. своп таблицы вокруг и сделать category на левое (при условии, каждый elements_categories имеет category) , Это не общее решение, но должно работать в этом случае.
  3. Сделайте временную таблицу с element_id и JOIN во всех ваших запросах, вместо IN (1,2,3...). Вы также должны использовать IN (SELECT id FROM <temp table>) вместо литералов.
+0

Спасибо, проблема в том, что я использую (действительно использую с продуманной фильтрацией не только для удобства) ORM, который трудно убедить в своп-таблицах и т. Д. –

0

Причина, по которой оптимизатор выбирает другой план, когда у вас есть разные параметры, что она смотрит на статистику из таблиц и угадать, какой индекс будет удалить большинство строк, но это угадать и часто может быть неправильным , Если вы знаете лучше, вам нужно сказать оптимизатору, что делать с подсказкой индекса, как в первом примере @Vatev.

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

Еще одна интересная особенность оптимизатора заключается в том, что если индекс содержит всю информацию, необходимую из таблицы, она может не искать реальную строку, поэтому в зависимости от вашей ситуации вы может извлечь дополнительную добавку в индекс. Эта оптимизация используется в первом плане запроса «с использованием индекса», но не во втором. Таким образом, добавление «element_id» к вашему индексу «elements_categories_category_id» может ускорить процесс. см. http://dev.mysql.com/doc/refman/5.6/en/explain-output.html

+0

Что касается индексов ... индекс «elements_categories_element_id» является дублировать, так как «UNIQUE KEY element_id» содержит надмножество этой информации. –

+0

Правда, это один из недостатков ORM, который тупо создает индексы для всех внешних ключей. Попробуете возиться с индексами и посмотреть, ускорит ли он это. –