Оптимизация простого запроса на двух больших таблицах - PullRequest
4 голосов
/ 17 июня 2009

Я пытаюсь предложить функцию, с помощью которой я могу показать страницы, которые чаще всего просматривают друзья. Моя таблица друзей имеет 5,7 млн ​​строк, а таблица просмотров - 5,3 млн строк. В данный момент я просто хочу выполнить запрос к этим двум таблицам и найти 20 самых просматриваемых идентификаторов страниц от друга человека.

Вот запрос, который у меня есть сейчас:

SELECT page_id 
FROM `views` INNER JOIN `friendships` ON friendships.receiver_id = views.user_id 
WHERE (`friendships`.`creator_id` = 143416) 
GROUP BY page_id 
ORDER BY count(views.user_id) desc 
LIMIT 20

А вот как выглядит объяснение:

+----+-------------+-------------+------+-----------------------------------------+---------------------------------+---------+-----------------------------------------+------+----------------------------------------------+
| id | select_type | table       | type | possible_keys                           | key                             | key_len | ref                                     | rows | Extra                                        |
+----+-------------+-------------+------+-----------------------------------------+---------------------------------+---------+-----------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | friendships | ref  | PRIMARY,index_friendships_on_creator_id | index_friendships_on_creator_id | 4       | const                                   |  271 | Using index; Using temporary; Using filesort | 
|  1 | SIMPLE      | views       | ref  | PRIMARY                                 | PRIMARY                         | 4       | friendships.receiver_id                 |   11 | Using index                                  | 
+----+-------------+-------------+------+-----------------------------------------+---------------------------------+---------+-----------------------------------------+------+----------------------------------------------+

Таблица представлений имеет первичный ключ (user_id, page_id), и вы можете видеть, что он используется. Таблица дружеских отношений имеет первичный ключ (receive_id, creator_id) и вторичный индекс (creator_id).

Если я выполню этот запрос без группировки и ограничения, для этого конкретного пользователя будет около 25 000 строк, что типично.

При последнем реальном запуске этот запрос выполнялся за 7 секунд, что слишком много для адекватного ответа в веб-приложении.

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

Можете ли вы увидеть, как можно переписать запрос, чтобы он быстро осветлялся?

Обновление: мне нужно больше тестировать, но, похоже, мой неприятный запрос работает лучше, если я не делаю группировку и сортировку в БД, а потом делаю это в ruby. Общее время намного короче - кажется, примерно на 80%. Возможно, мое раннее тестирование было ошибочным - но это определенно требует дополнительного расследования. Если это правда - то что делает Mysql?

Ответы [ 3 ]

1 голос
/ 17 июня 2009

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

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

Сводные таблицы являются ключом к поддержанию хорошей производительности для запросов типа агрегации в средах, предназначенных главным образом для чтения. Вы выполняете работу заранее, когда обновления происходят (нечасто), а затем запросы (часто) не должны выполнять какую-либо работу.

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

0 голосов
/ 18 июня 2009

Ваши индексы выглядят корректно, хотя, если friendship имеет очень большие строки, вы можете захотеть, чтобы индекс на (creator_id, receiver_id) избегал чтения всего этого.

Однако что-то здесь не так, почему вы делаете сортировку файлов для 271 строки? Убедитесь, что ваш MySQL имеет по крайней мере несколько мегабайт для tmp_table_size и max_heap_table_size. Это должно сделать GROUP BY быстрее.

sort_buffer также должен иметь вменяемое значение.

0 голосов
/ 17 июня 2009

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

Вы можете легко поддерживать эту таблицу, используя триггер для таблицы представлений, который обновляет таблицу 'views_summary' всякий раз, когда происходит вставка в таблицу 'представлений'.

Возможно, вы даже сможете еще больше денормализовать это, взглянув на реальные отношения, или просто сохраняйте верхние x страниц на человека

Надеюсь, это поможет,

Эверт

...