2017-02-15 37 views
0

Предположим, у меня три таблицы - пользователи, серверы и платежи. Каждый пользователь может иметь несколько серверов, и каждый сервер может иметь несколько платежей. Давайте также скажем, что я хотел найти самые последние платежи и получить информацию о серверах/клиентах, к которым прилагаются эти платежи. Вот запрос, который мог бы сделать это:СОБЫТИЯ, выполняемые в странном порядке; испортить ORDER BY?

SELECT * 
FROM payments p 
JOIN customers c ON p.custID = c.custID 
JOIN servers s ON s.serverID = p.serverID 
WHERE c.hold = 0 
    AND c.archive = 0 
ORDER BY p.paymentID DESC 
LIMIT 10; 

Проблема заключается в том, что, когда я бегу EXPLAIN на этот запрос я получаю это:

id select_type table type possible_keys   key    key_len ref     rows  Extra 
1 SIMPLE  c  ref PRIMARY,hold_archive  hold_archive  3   const,const   28728 Using where; Using index; Using temporary; Using filesort 
1 SIMPLE  p  ref custID     custID    5   customers.custID 3  Using where 
1 SIMPLE  s  eq_ref PRIMARY     PRIMARY   4   payments.serverID 1  Using index 

Проблема заключается в том, что запрос занимает некоторое время для запуска. Если я удалю ORDER BY, он станет на 10x быстрее. Но мне нужен ORDER BY. Вот EXPLAIN, когда я удаляю ORDER BY:

id select_type table type possible_keys   key    key_len ref     rows  Extra 
1 SIMPLE  c  ref PRIMARY,hold_archive  hold_archive  3   const,const   28728 Using where; Using index 
1 SIMPLE  p  ref custID     custID    5   customers.custID 3  Using where 
1 SIMPLE  s  eq_ref PRIMARY     PRIMARY   4   payments.serverID 1  Using index 

Так что большая разница в том, что «Использование временного» и «Использование FileSort» отсутствуют в дополнительной колонке.

Похоже, в этом случае причина в том, что столбец, в котором я выполняю ORDER BY, не является первым столбцом в EXPLAIN.

Другое наблюдение. Если я удалю одно из предложений WHERE (при сохранении ORDER BY), он ускорится схожим образом, но мне нужны оба ГДЕ. Ниже приведен пример того, что EXPLAIN:

id select_type table type possible_keys   key    key_len ref     rows  Extra 
1 SIMPLE  p  index custID,serverID   PRIMARY   4   NULL    10  Using where 
1 SIMPLE  c  eq_ref PRIMARY,hold_archive  PRIMARY   4   payments.custID  1  Using where 
1 SIMPLE  s  eq_ref PRIMARY     PRIMARY   4   payments.serverID 1  Using index 

Здесь ORDER BY столбца/в/делается на первом столбце EXPLAIN. Но почему MySQL переустанавливает порядок, в котором находятся JOINed, и как я могу сделать это, чтобы он этого не делал? Вы можете форсировать индексы в MySQL, но похоже, что это не помогло бы.

Любые идеи?

+0

С такими вопросами вам также необходимо предоставить инструкции SHOW CREATE TABLE для всех соответствующих таблиц. – Strawberry

+1

Быстрое предположение: если нет предложения 'ORDER BY', то сервер возвращает вам грамотно первые 10 записей, которые соответствуют критериям и перестает обрабатываться после достижения этого числа. Вероятно, он начинается с таблицы 'customer', потому что на этом есть фильтр, а затем ищет связанные« платежи », а затем и« серверы », которые соответствуют. Хорошо работает. Однако, когда вы добавляете 'ORDER BY', тогда сначала нужно составить список * all * соответствующих записей (' c' => 'p' =>' s'), сортировать их на 'paymentID' а затем выберите первые 10 из этого списка, начиная с «самого низкого». – deroby

ответ

1

10 раз быстрее - он может найти «любые 10 строк» ​​намного быстрее, чем «найти все возможные строки, отсортировать их, а затем доставить 10».

Имея WHERE и ORDER BY ударить по различным колонкам трудно оптимизировать.

Какой процент платежей имеет hold=0 and archive=0? Это звучит как небольшой процент? Сколько строк в каждой таблице?

Что-нибудь еще нужно INDEX(hold, archive)? Если нет, избавитесь от него. Кажется, это только вызывает проблемы.

Если hold=0 and archive=0 является обычным явлением, вы бы предпочли, чтобы выполнение было похоже на ваш третий EXPLAIN - это сканирование payments в порядке убывания. Поскольку большинство из них соответствуют WHERE, оно должно обычно `нужно поразить не более 10 строк, прежде чем найти 10 соответствующих строк.

Другим решением (кроме исключения из индекса) является изменение JOIN до STRAIGHT_JOIN в запросе. Это говорит оптимизатору, что вы знаете лучше, и payments следует отсканировать сначала, customers секунд. Это хорошо работает, если применяется мой предыдущий параграф.

Но запрос будет испорчен (если быть медленным), если, скажем, вы ищете archive=1.