MySQL: почему процедура выполняется медленнее, чем выполнение запроса?

Итак, у меня есть этот супер-дупер-запрос, который находит «связанные статьи» на основе количества тегов, которые они имеют вместе с исходной статьей (предоставляется в переменной $id). Я не знаю, важен ли сам запрос, но это Джастин Кейс.

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

Почему? Это потому, что он возвращает несколько строк? Процедуры воняют при этом? Это потому, что я должен использовать входную переменную в своей процедуре? 450 это куча!

SELECT a.id, a.image, a.title, a.excerpt, a.permalink, COUNT(rel.category_id) AS n
  FROM articles AS a
  JOIN category_relations AS rel ON rel.article_id = a.id
  JOIN categories AS c ON rel.category_id = c.id
 WHERE rel.category_id IN (SELECT category_id 
                             FROM category_relations 
                            WHERE article_id = {$id})
   AND a.id != {$id}
   AND c.type = 1
GROUP BY rel.article_id
ORDER BY n DESC, publish_date DESC
   LIMIT 10

Код, используемый для создания процедуры:

DROP PROCEDURE IF EXISTS get_related_articles;
DELIMITER //
CREATE PROCEDURE get_related_articles(IN id INT)
BEGIN
   SELECT   a.id, a.image, a.title, a.excerpt, a.permalink, COUNT(rel.category_id) AS n
   FROM     articles AS a
    JOIN  category_relations AS rel ON rel.article_id = a.id
    JOIN  categories AS c ON rel.category_id = c.id
   WHERE    rel.category_id IN ( SELECT category_id FROM category_relations WHERE article_id = id)
    AND   a.id != id
    AND   c.type = 1
   GROUP BY rel.article_id
   ORDER BY n DESC, publish_date DESC
   LIMIT    10;
END //
DELIMITER ;

person 3Nex    schedule 07.03.2013    source источник
comment
Я не уверен, где вы слышали, что они должны быть быстрее (какой единицей измерения вы пользуетесь для измерения 450? 450 верблюдов?). Я также не знаю, кто такой Джастин Кейс. В каком контексте вызывается хранимая процедура? Похоже, вы не можете использовать их правильно.   -  person Kermit    schedule 07.03.2013
comment
Кэширование запросов происходит на основе всего запроса, включая значения, предоставленные вашей переменной PHP. Поэтому при изменении переменной кеш запросов нельзя использовать повторно. Кэширование запросов также чувствительно к регистру.   -  person OMG Ponies    schedule 07.03.2013
comment
@AarolamaBluenk Вам не нужны единицы для коэффициентов умножения. Если вы умножите значение только на число, вы получите ту же единицу измерения, что и исходное значение. Джастин Кейс — супергерой, который появляется только тогда, когда это необходимо (то есть — на всякий случай). Контекст просто CALL get_related_articles(17232). Этот вызов выполняется очень близко к одной секунде, в то время как выполнение фактического запроса занимает около 2 мс.   -  person 3Nex    schedule 07.03.2013
comment
@3Nex Как $id вписывается в ваш запрос? Вы действительно сравнивали свой запрос?   -  person Kermit    schedule 07.03.2013
comment
@AarolamaBluenk Забыл сказать, что это строка из кода PHP, поэтому MySQL получит фактическое целое число.   -  person 3Nex    schedule 07.03.2013
comment
Что такое n в ORDER BY n DESC?   -  person Kermit    schedule 07.03.2013
comment
@AarolamaBluenk О Боже, извините .... Я удалил некоторые поля, прежде чем публиковать здесь запрос, потому что их было много. Я отредактировал запрос, и теперь n находится в списке полей. Что касается бенчмаркинга, Phpmyadmin сообщил мне время выполнения. И столько раз, сколько я запускал оба, процедура остается очень близкой к 1 секунде, запрос - около 2 мс.   -  person 3Nex    schedule 07.03.2013
comment
Видишь ли, я понимаю, что переменная не может быть кэширована, но не имеет смысла, что процедура в 450 раз медленнее, когда запрос должен быть переведен в ОБОИХ случаях. Что делает процедуру намного медленнее? Я бы предположил, что в худшем случае они должны работать одинаково.   -  person 3Nex    schedule 07.03.2013
comment
Кажется, что мы упускаем какую-то часть головоломки. Невозможность увидеть, как выглядит хранимая процедура точно, делает эту игру угадыванием.   -  person Kermit    schedule 07.03.2013
comment
Добавлен код процедуры   -  person 3Nex    schedule 07.03.2013
comment
@AarolamaBluenk, обычно хранимые процедуры считаются более быстрыми, потому что код хранится на сервере, поэтому используется меньше трафика.   -  person b.b3rn4rd    schedule 07.03.2013


Ответы (2)


Добро пожаловать в реальный мир MySQL! Иногда очень сложно сказать, почему один запрос выполняется дольше, чем другой. Но в вашем случае ответ можно найти здесь:

MySQL не использует кеш для запросов, которые были вызваны из хранимых процедур.

person ravnur    schedule 07.03.2013

Это не положительно скажется на вашей задержке, но IN SUBSELECTS сами по себе могут быть дорогостоящими. Думали ли вы о присоединении к тому, что теперь является вашим подвыбором? Кроме того, поскольку таблица статей является основой запроса, а a.id = rel.article_id, ваша группа по «a.id» может быть лучше, если она доступна через индекс.

SELECT 
      a.id, 
      a.image, 
      a.title, 
      a.excerpt, 
      a.permalink
   FROM 
      articles AS a
         JOIN category_relations AS rel 
            ON a.id = rel.article_id 
            JOIN categories AS c 
               ON rel.category_id = c.id
               AND c.type = 1
            JOIN (SELECT category_id 
                     FROM admin_category_relations 
                     WHERE article_id = {$id} ) RelByArticle
               on rel.category_id = RelByArticle.category_id
   WHERE 
      a.id != {$id}
   GROUP BY 
      a.id
   ORDER BY 
      n DESC, 
      publish_date DESC
   LIMIT 10
person DRapp    schedule 07.03.2013
comment
Как этот ответ объясняет проблему OP с медленной хранимой процедурой? - person Kermit; 07.03.2013
comment
@AarolamaBluenk, вы правы, но иногда альтернативное написание запросов, которые лучше оптимизированы для индексов, может просто работать лучше, независимо от того, что происходит. ИНОГДА это просто вопрос выборки. - person DRapp; 07.03.2013
comment
Я думаю, что большая проблема здесь заключается в том, что OP требуется всего 10 записей, но при этом он извлекает полные наборы результатов при соединениях. - person Kermit; 07.03.2013
comment
Что ж, время выполнения этого запроса, в отличие от моего, почти такое же, так что никаких улучшений нет. Комментарий @AarolamaBluenk, я не знаю, что мне нужно изменить, чтобы избежать ненужных наборов результатов? - person 3Nex; 07.03.2013
comment
Нет необходимости в производном табличном/встроенном представлении (JOIN (...)) - это можно сделать как критерий INNER JOIN. Но использование JOIN может привести к умножению записей, если родительская запись содержит более одного дочернего элемента. - person OMG Ponies; 07.03.2013
comment
@AarolamaBluenk Хорошо, но не могли бы вы сказать мне, что именно нужно изменить? Я понял, о чем вы говорите, но я недостаточно знаю MySQL, чтобы добиться этого самостоятельно. - person 3Nex; 07.03.2013