2016-01-31 2 views
1

Я создал простую базу данных (innodb version 5.7.9) с двумя таблицами, post и post_tag.Запрос MySQL быстрее в порядке DESC, чем заказ ASC

Сообщение имеет единственный идентификатор поля (большой int), заданный как первичный ключ (около 120 000 записей). Post_tag имеет 2 поля, post_id (большой int) и tag_id (int), а первичный ключ - [post_id, tag_id].

Следующий запрос выполняется в ~ 1мс:

SELECT 
    SQL_NO_CACHE p.id 
FROM 
    post as p 
STRAIGHT_JOIN 
    post_tag t 
WHERE 
    t.post_id = p.id AND t.tag_id = 25 
ORDER BY 
    p.id DESC 
LIMIT 0, 100 

Но если я изменю ORDER BY для ASC, он работает примерно в 100 раз медленнее! И то, что меня интересует ...

Любая идея, почему?

Первоначально я хотел, чтобы идентификаторы отсортированы по DESC, и я заметил, что он был медленнее, чем ASC. Я читал, что естественная сортировка для индекса - ASC, поэтому я вернул все ID (выполнив ID = SOMETHING BIG - ID), но потом он ничего не изменил, поскольку в ASC теперь он медленнее.

Я загрузил базу данных here в случае, если это полезна.

Большое спасибо заранее всем, кто может помочь.

А вот объясните: enter image description here

+1

Меняет 'WHERE' на' ON' и просто выполняет регулярную 'JOIN' вместо' STRAIGHT_JOIN' что-то меняет? –

+0

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

+0

@JoachimIsaksson, меняющий, где ON, ничего не меняет. Выполнение регулярного соединения изменит план выполнения, чтобы начать с таблицы post_tag, и это не то, что я хочу. Причина в том, что моя фактическая база данных (то, что я разместил здесь, это просто урезанный пример) имеет более сложный индексный ключ, для которого требуется запустить план выполнения по столбике. И на самом деле в этом случае мне не нужно прямое соединение, так как mysql это понимает. Таким образом, просто я просто поставил прямое соединение под тем же условием, что и моя фактическая база данных. – maalls

ответ

1

Если есть «другие ограничения», тогда все ставки отключены.

Между тем, глядя на то, что у вас есть ...

STRAIGHT_JOIN, USE INDEX и т.д., являются костылей, когда (а) не имеют индекс «правильный», или (б) оптимизатор может» t выяснить, что нужно делать. То есть, ищите другие решения.

В вашем примере вам будет лучше с равными JOIN и INDEX(tag_id, post_id). Это позволило бы довести до post_tagпервый, так как есть статья WHERE, позволяющая фильтровать там. Оптимизатор, вероятно, увидит, что t.post_id и p.id идентичны, поэтому запустите конец (для DESC) (25, post_id) в индексе и сканируйте.Затем он проверяет, есть ли запись post (это единственное видимое использование для post - снова, если есть «другие ограничения», все ставки отключены).

Итак, вернемся к исходному вопросу. STRAIGHT_JOINвынужден смотреть в post первый. Но где 25-е? По-видимому, вблизи конец от post_tag. Следовательно, ASC потребовалось больше времени, чтобы найти 100 (см. LIMIT), чем если сканирование началось с другого конца!

Предполагая, что это многие-ко-многим таблицы отображения, сделайте следующее:

CREATE TABLE post_tag (
    post_id ..., 
    tag_id ..., 
    PRIMARY KEY(post_id, tag_id), 
    INDEX  (tag_id, post_id) 
) ENGINE=InnoDB; 

Я обсуждаю много причин, в my blog.

Если, как было предложено, вы добавляете (tag_id, post_id DESC), не вводите в заблуждение, думая, что DESC означает что-либо - оно распознается, но игнорируется. Оба детали будут сохранены ASC. Что произойдет, так это то, что Оптимизатор достаточно умен, чтобы начать в конце 25-х и отсканировать назад. Вот «доказательство»:

US имеет INDEX(state, population):

mysql> FLUSH STATUS; 
mysql> SELECT city, population FROM US 
      WHERE state = 'OH' 
      ORDER BY population DESC LIMIT 5; 
+------------+------------+ 
| city  | population | 
+------------+------------+ 
| Columbus |  736836 | 
| Cleveland |  449514 | 
| Toledo  |  306974 | 
| Cincinnati |  306382 | 
| Akron  |  208414 | 
+------------+------------+ 
mysql> SHOW SESSION STATUS LIKE 'Handler%'; 
| Handler_read_key   | 1  | -- get started at end of Ohio 
| Handler_read_prev   | 4  | -- read (5-1) more, scanning backwards 

Единственный случай, когда MySQL отсутствует лодку, игнорируя DESC в INDEX декларации: ORDER BY a ASC, b DESC не может использовать INDEX(a,b).

+0

Вы совершенно правы, 25-е находятся в конце списка, что объясняет, почему требуется больше времени для выполнения по порядку, чем другой. Большое спасибо! – maalls

0

Предположительно, у вас есть индекс по post(id) (это автоматически создается для первичных ключей, например). MySQL иногда обращает внимание на порядок индекса при использовании индекса для ORDER BY.

Изменяя заказ, вы меняете план запроса таким образом, чтобы сортировка была необходима.

Я хотел бы предложить писать запрос, используя только одну таблицу:

SELECT t.post_id 
FROM post_tag t 
WHERE t.tag_id = 25 
ORDER BY t.post_id DESC 
LIMIT 0, 100; 

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

Для этого запроса индекс на post_tag(tag_id, post_id desc) является оптимальным, и MySQL может сделать правильную вещь для нисходящей сортировки.

+0

Как я уже говорил, у меня есть первичный индекс в post (id). Спасибо за предложение, но я хотел бы решить проблему, понимая, почему она медленнее в ASC, чем DESC, вместо изменения запроса. Причина в том, что я не могу применить ваше предложение в моей реальной базе данных, которая имеет другие ограничения. – maalls

+0

Но запрос, который вы предлагаете, довольно интересен тем, что он примерно в 10 раз быстрее в DESC (~ 1 мс), чем в порядке ASC (~ 10 мс). Любая идея почему? – maalls

+0

@maalls. , , Вероятно, это лишь незначительная неэффективность при чтении индекса назад. Времена очень малы, поэтому разница в 10x, вероятно, не имеет смысла. Такие небольшие различия могут быть вызваны такими вещами, как кеширование или оптимизация чтения страницы с перспективой. –