MySQL: медленные запросы с использованием «group by» - застрял в «Копирование в таблицу tmp» - PullRequest
1 голос
/ 17 января 2012

Я работаю над «таблицей лидеров» для веб-приложения, связанного со спортивным событием, которое сообщает о 20 лучших пользователях на основе их оценок по всем их ответам на тест с несколькими вариантами ответов.Он также отображает собственный рейтинг текущего пользователя в таблице лидеров.

Когда это приложение тестируется под нагрузкой, два рассматриваемых запроса становятся очень медленными, тратя много времени в состоянии «Копирование в таблицу tmp» (до20 секунд на запрос).Они в конечном итоге обрабатываются, но в то же время сотни могут складываться.

В изоляции, учитывая разумное количество строк в таблице ответов, каждому запросу требуется около 1 секунды для выполнения (25 000 пользователей, например 200 000 строк в ответах)

Я добавил некоторые индексы в соответствующие таблицы, особенно для столбцов FK и всего, что используется в операторах where.Я также добавил индекс покрытия для userID, answerID в таблице ответов.

Это запрос самой таблицы лидеров

SELECT users.username, sum(questions.points) as score FROM responses
JOIN answers on responses.answerID = answers.answerID
JOIN questions on answers.questionID = questions.questionID
JOIN users on responses.userID = users.userID
WHERE users.username != '' AND answers.isCorrect  
GROUP BY users.userID
ORDER BY score DESC
LIMIT 20

Это запрос для получения собственного рейтинга пользователя.в пределах результатов;сначала отдельный запрос получает их оценку, а затем мы подсчитываем, сколько пользователей имеют более высокие оценки.

Select count(*) +1 as rank  from (
    SELECT users.username, sum(questions.points) as score
    FROM responses
    JOIN answers on responses.answerID = answers.answerID
    JOIN questions on answers.questionID = questions.questionID
    JOIN users on responses.userID = users.userID
    WHERE users.username != '' AND answers.isCorrect 
    GROUP BY users.userID
    HAVING sum(questions.points) > 2431
    ORDER BY score DESC
) as result

Упрощенная схема - это

QUESTIONS
questionID
question
points

ANSWERS (multiple choice answers for question)
answerID
questionID
answer
isCorrect

RESPONSES (the player's choice of answer)
responseID
answerID
userID

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

Кроме того, есть ли у кого-нибудь мысли о том, почему эти запросы складываются в "Копирование в таблицу tmp""состояние и просто так долго обрабатывать, когда сервер находится под нагрузкой?Я думал, что это может быть создание их на диске, но я вижу, что это отдельное сообщение о состоянии.Я использовал EXPLAIN, но я чувствую, что временная таблица неизбежна при этих запросах;следовательно, вопрос «Копирование в таблицу tmp» занимает столько времени

Ограничения: не показаны, пользователи имеют идентификаторы teamID, а запросы также фильтруются по teamID.Также не показано, есть несколько событий, и эти запросы также могут быть отфильтрованы по eventID.Кроме того, не все вопросы имеют правильный ответ на момент получения ответа.Правильные ответы могут быть назначены в будущем, но в любом случае в конце спортивного события.Система сообщает процент пользователей, выбирающих каждый ответ.Поэтому были рассмотрены различные способы хранения результатов в более агрегированном виде, но они отброшены, поскольку они конфликтуют с одним или несколькими из этих ограничений.

Надеюсь, этого достаточно для продолжения - большое спасибо

Ответы [ 2 ]

2 голосов
/ 17 января 2012

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

Хорошо для того, чтобы отловить его в нагрузочном тестировании, а не в производстве.

Как решить эту проблему?

  1. создайте сводную таблицу, в которой есть те же столбцы, что и ваш результат сводного запроса.
  2. создайте хранимую процедуру для извлечения сводных данных из ваших данныхтаблицы и переписать сводную таблицу.
  3. создайте событие для запуска хранимой процедуры с соответствующим интервалом.Насколько устаревшей может быть ваша таблица лидеров?Шесть секунд, минуту, час?Вот так часто должно проходить ваше мероприятие.Ваша проблема не в базовой стоимости запроса на извлечение списка лидеров.Проблема заключается в том, что вы пытаетесь запускать его миллион раз в минуту.
  4. переписать свой список лидеров, чтобы вытащить материал из сводной таблицы.

Таким образом, вы делаете сложноевещи раз и навсегда, и простые вещи для каждого пользователя.

Это стабилизирует ваше приложение и позволит ему хорошо масштабироваться.

0 голосов
/ 08 августа 2012

Взгляните на http://dev.mysql.com/doc/refman/5.5/en/internal-temporary-tables.html

MySQL использует временную таблицу, если есть предложение ORDER BY и другое предложение GROUP BY, а также в других перечисленных там случаях. Вы не можете обойти это.

Так что в вашем случае самое простое решение для этого может быть установка RAM-диска и заставить MySQL хранить там временные таблицы, как описано в:

пропустить копирование в таблицу tmp на диске mysql

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...