2016-07-20 1 views
1

У меня есть запрос:Почему запрос MySQL замедляется при использовании LIMIT с помощью Order BY?

SELECT abt.duration, a.id, a.a_id, a.a_tag 
    FROM active_begin_times AS abt INNER JOIN sources AS a ON a.id = abt.a_source 
    AND a.u IN (29, 28, 27, 26, 25, 24) 
    WHERE (abt.duration > 86400000) 
    and (abt.begin_timestamp<=1465185600000 and 1465617600000<=abt.end_timestamp 
    OR 1465185600000<=abt.begin_timestamp and abt.begin_timestamp<=1465617600000 
    OR 1465185600000<=abt.end_timestamp and abt.end_timestamp<=1465617600000) 
    order by abt.begin_timestamp asc LIMIT 0, 10 

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

SELECT abt.duration, a.id, a.a_id, a.a_tag 
FROM active_begin_times AS abt INNER JOIN sources AS a ON a.id = abt.a_source 
AND a.u IN (29, 28, 27, 26, 25, 24) 
WHERE (abt.duration > 86400000) 
    and (abt.begin_timestamp<=1465185600000 and 1465617600000<=abt.end_timestamp 
    OR 1465185600000<=abt.begin_timestamp and abt.begin_timestamp<=1465617600000 
    OR 1465185600000<=abt.end_timestamp and abt.end_timestamp<=1465617600000) 
    order by abt.duration asc LIMIT 0, 10 

У меня есть индексы на обоих продолжительности и на begin_timestamp. И когда я изменил порядок на abt.id, для вычисления снова потребовалось много времени, которое также имеет индексы.

Кроме того, что я заметил для этого конкретного набора условий, запрос возвращает 2 строки. Но если я изменю переменные, и я получаю этот запрос, чтобы вернуть от 20 до 30 нечетных строк, то вычисления будут мгновенными.

Не могли бы вы объяснить, что перечисленные выше 2 случая? Я пробовал смотреть в ТО, но не мог либо понять поведение, либо не был удовлетворен объяснением.

EXPLAIN для первых возвращений запроса:

| id | select_type | table | type | possible_keys           | key    | key_len | ref     | rows | Extra  | 
|----|-------------|-------|--------|----------------------------------------------------------|-----------------|---------|-----------------------|------|-------------| 
| 1 | SIMPLE  | abet | index | FK2681F9347A6A34B,begin_timestamp,end_timestamp,duration | begin_timestamp | 9  | \N     | 6094 | Using where | 
| 1 | SIMPLE  | a  | eq_ref | PRIMARY,FK722DBCA9F603AF         | PRIMARY   | 4  | db.abt.alarm_source | 1 | Using where | 

А из второго запроса я получаю:

| id | select_type | table | type | possible_keys           | key  | key_len | ref     | rows | Extra        | 
|----|-------------|-------|--------|----------------------------------------------------------|----------|---------|-----------------------|------|------------------------------------| 
| 1 | SIMPLE  | abet | range | FK2681F9347A6A34B,begin_timestamp,end_timestamp,duration | duration | 9  | \N     | 8597 | Using index condition; Using where | 
| 1 | SIMPLE  | a  | eq_ref | PRIMARY,FK722DBCA9F603AF         | PRIMARY | 4  | db.abt.alarm_source | 1 | Using where      | 

Я видимое различие я вижу в Extras столбцах, где в 2 запроса говорит Using index condition;, бит, я не уверен, что с этим делать.

Выход SHOW CREATE TABLE active_begin_time:

CREATE TABLE active_begin_times (
    id int(11) NOT NULL AUTO_INCREMENT, 
    begin_timezone_offset int(11) DEFAULT NULL, 
    begin_timezone_suffix varchar(100) DEFAULT NULL, 
    begin_timestamp bigint(20) DEFAULT NULL, 
    begin_timestamp_date varchar(100) DEFAULT NULL, 
    duration bigint(20) DEFAULT NULL, 
    end_timezone_offset int(11) DEFAULT NULL, 
    end_timezone_suffix varchar(100) DEFAULT NULL, 
    end_timestamp bigint(20) DEFAULT NULL, 
    end_timestamp_date varchar(100) DEFAULT NULL, 
    incomplete int(11) DEFAULT NULL, 
    a_source int(11) DEFAULT NULL, 
    PRIMARY KEY (id), 
    KEY FK2681F9347A6A34B (alarm_source), 
    KEY begin_timestamp (begin_timestamp), 
    KEY end_timestamp (end_timestamp), 
    KEY begin_timezone_offset (begin_timezone_offset), 
    KEY end_timezone_offset (end_timezone_offset), 
    KEY duration (duration), 
    KEY begin_timestamp_date (begin_timestamp_date), 
    KEY end_timestamp_date (end_timestamp_date) 
) ENGINE=MyISAM AUTO_INCREMENT=6164640 DEFAULT CHARSET=latin1 
+0

Можете ли вы опубликовать вывод SHOW CREATE TABLE active_begin_times? – olegsv

+0

только что обновил его. – rd22

+0

Я бы предложил переписать запрос, используя BETWEEN: '(abt.end_timestamp BETWEEN 1465185600000 и 1465617600000)' – olegsv

ответ

0

, потому что ваша таблица имеет индекс по duration (не другой), он получает первые 10 строк, и делается. Фактически, на нем есть индекс covering, uber fast. Он является частью ключа composite, удовлетворяющего всем запросам без перехода на страницу данных. Конечно, есть соединение. В ней задействованы две таблицы.

+0

Привет, Дрю, я не понял, что вы подразумеваете под «потому что в вашей таблице есть индекс продолжительности (а не другой)»? – rd22

+0

Ах, если бы у вас была 'key \' comp \ '(begin_timestamp, end_timestamp, duration)', это был бы составной составной элемент, и вы могли бы вырезать текущий ключ только на 'begin_timestamp', потому что этот новый состав будет охватывать это как это самый левый (первый). Тем не менее, будьте осторожны, чтобы не переоценивать, особенно для редкого использования. Поскольку индексы замедляют вставки и * могут * замедлять обновления. Индексы могут замедлять обновления только при изменении ключевой части, а не в не-ключевой части. Индексы могут ускорять обновления, когда ключи используются для поиска данных для обновления. Нужно всегда пересматривать свои варианты выбора – Drew

+0

Тень и я торгуются об этом неделю назад [здесь] (http://stackoverflow.com/questions/38268050/innodb-composite-indexing-order-and-insert-performance/38268187# комментарий63984651_38268187). Немного связано, совсем немного. Нет двух одинаковых ситуаций. Поэтому, чтобы быть понятным, только вы должны решить, что вы хотите и знаете, лучше всего подходит для выбора вашей системы и индекса. Я стараюсь всегда выкидывать высказывания оговорки, прежде чем я пойду и скажу «просто создайте этот новый индекс». В противном случае это дешево и безответственно. – Drew

1

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

Второй гораздо быстрее, потому что он использует индекс для ограничения строк в первую очереди (type: range) и он делает это на уровне хранения двигателя (extras: using index condition) без чтения целых строк таблицы для проверки (abt.duration > 86400000). Для тех строк, которые соответствуют первому условию, полные строки таблицы считываются для проверки на остальную часть предложения where.

Не смотря на объяснения результатов, если сравнить две части, где положение, это легко можно использовать индекс для первой части (abt.duration > 86400000), но не для второй части, где вы оба имеют abt.begin_timestamp<=1465185600000 и 1465185600000<=abt.begin_timestamp и даже третье условие на end_timestamp не begin_timestamp.

Дальнейшее чтение: Index Condition Pushdown Optimization

1
WHERE duration > 1234 AND ... 
ORDER BY duration LIMIT 10 

можно очень эффективно использовать INDEX(duration):

  1. Locate, в индексе, 1234
  2. Grab следующие 10 строк, которые удовлетворяют остальной WHERE. Затем STOP. Примечание. Если есть другая фильтрация (AND ...), и, скоро появится 10 строк, это быстро. Если, с другой стороны, 10 строк идут намного позже в таблице, это происходит медленно.

Ваш другой случай более сложный, так как begin_timestamp не является простым предложением where. Вместо этого он должен:

  1. Найти все строки, соответствующие WHERE (EXPLAIN оценки тысячи).
  2. Сортировка по begin_timestamp (индекс не является полезным)
  3. доставить 10 строк.

Фактически, это может быть быстрее, чем «медленный» случай, выше. Это зависит от распределения значений данных и других вещей, о которых оптимизатор не знает достаточно. Итак ... Будут моменты, когда оптимизатор выбирает «неправильный» способ оценки такого рода запросов и заканчивается медленнее, чем должно быть.

Комментарий к индексу 'cover' был неправильным, так как он не включает a_source.

+0

Тогда почему первый запрос быстрее, когда он выводит больше строк, случай, о котором я упоминал в вопросе? Существует еще одно замечание, когда предел изменяется на «LIMIT 0, 1000', первый запрос занимает всего 2 секунды для вычисления. – rd22

+0

Смотрите мои правки. Это может быть так, что оптимизатор сказал: «О, мне нужно« много »строк, поэтому я буду использовать механизм сортировки. И это происходит быстрее. –

+0

О, упрощение: OR могут быть устранены: '(begin <14656 ... AND 14651 ...