2012-01-17 2 views
1

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

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

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

Я добавил некоторые индексы для таблиц, связанных, в частности, для столбцов 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 и запросы, также отфильтрованные командойID. Также не показано, есть несколько событий, и эти запросы также могут быть отфильтрованы eventID. Кроме того, не все вопросы имеют правильный ответ в момент ответа. Правильные ответы могут быть назначены в будущем, но, во всяком случае, в конце спортивного события. Система сообщает проценты пользователей, которые выбирают каждый ответ. Поэтому различные способы хранения оценок более агрегированным образом были рассмотрены, но отброшены, поскольку они противоречат одному или нескольким из этих ограничений.

Надеется, что это достаточно, чтобы идти дальше - большое спасибо

ответ

2

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

Хорошо, если вы поймаете его при испытании нагрузки, а не в производстве.

Как вы это решаете?

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

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

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

+0

Спасибо за это; оцените ваш ответ. Я считал, что внедрение чего-то смутно похожее, но что происходит, когда во время запроса таблицы рефинансирования приходит запрос на ведение списка? – Polsonby

+0

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

0

Посмотрите на http://dev.mysql.com/doc/refman/5.5/en/internal-temporary-tables.html

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

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

skip copying to tmp table on disk mysql

Смежные вопросы