Я работаю над «листинговой панелью» для веб-приложения, относящегося к спортивному событию, которое сообщает 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. Кроме того, не все вопросы имеют правильный ответ в момент ответа. Правильные ответы могут быть назначены в будущем, но, во всяком случае, в конце спортивного события. Система сообщает проценты пользователей, которые выбирают каждый ответ. Поэтому различные способы хранения оценок более агрегированным образом были рассмотрены, но отброшены, поскольку они противоречат одному или нескольким из этих ограничений.
Надеется, что это достаточно, чтобы идти дальше - большое спасибо
Спасибо за это; оцените ваш ответ. Я считал, что внедрение чего-то смутно похожее, но что происходит, когда во время запроса таблицы рефинансирования приходит запрос на ведение списка? – Polsonby
Вы используете InnoDB? Если это так, ваш запрос на обновление заблокирует вашу сводную таблицу, и пользовательские запросы будут зависать на секунду или около того, чтобы произвести ее, а затем закончить нормально. Если вы используете MyISAM, ваша хранимая процедура должна, вероятно, заблокировать сводную таблицу, чтобы получить тот же эффект. Если все это представляет собой ужасную проблему, вы можете попробовать создать новую таблицу, затем заблокировать старый, удалить его и переименовать в новое имя. Но это полный шарик для отладки. –