MySQL Медленно при соединении. Любой способ ускорить

У меня 2 стола. 1 — музыка, 2 — listenTrack. listenTrack отслеживает уникальное воспроизведение каждой песни. Я пытаюсь получить результаты для популярных песен месяца. Я получаю результаты, но они занимают слишком много времени. Ниже мои таблицы и запрос

430 000 строк

CREATE TABLE `listentrack` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `sessionId` varchar(50) NOT NULL,
    `url` varchar(50) NOT NULL,
    `date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `ip` varchar(150) NOT NULL,
    `user_id` int(11) DEFAULT NULL,
     PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=731306 DEFAULT CHARSET=utf8

12500 строк

CREATE TABLE `music` (
   `music_id` int(11) NOT NULL AUTO_INCREMENT,
   `user_id` int(11) NOT NULL,
   `title` varchar(50) DEFAULT NULL,
   `artist` varchar(50) DEFAULT NULL,
   `description` varchar(255) DEFAULT NULL,
   `genre` int(4) DEFAULT NULL,
   `file` varchar(255) NOT NULL,
   `url` varchar(50) NOT NULL,
   `allow_download` int(2) NOT NULL DEFAULT '1',
   `plays` bigint(20) NOT NULL,
   `downloads` bigint(20) NOT NULL,
   `faved` bigint(20) NOT NULL,
   `dateadded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
   PRIMARY KEY (`music_id`)
) ENGINE=MyISAM AUTO_INCREMENT=15146 DEFAULT CHARSET=utf8


SELECT COUNT(listenTrack.url) AS total, listenTrack.url 
FROM listenTrack
LEFT JOIN music ON music.url = listenTrack.url
WHERE DATEDIFF(DATE(date_created),'2009-08-15') = 0
GROUP BY listenTrack.url
ORDER BY total DESC
LIMIT 0,10

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

Есть ли способ ускорить это? Или вы можете предложить лучшее решение? Это будет работа cron в начале каждого месяца, но я также хотел бы делать это по результатам дня.

О, кстати, я запускаю это локально, более 4 минут для запуска, но в продукте это занимает около 45 секунд.


person Khary    schedule 18.08.2009    source источник


Ответы (9)


Я больше разбираюсь в SQL Server, но эти концепции должны применяться.

Я бы добавил индексы:

  1. В ListenTrack добавьте индекс с URL-адресом и date_created
  2. В Музыке добавьте индекс с URL

Эти индексы должны значительно ускорить выполнение запроса (первоначально я перепутал имена таблиц - исправлено в последнем редактировании).

person Jeff Siver    schedule 18.08.2009
comment
Я считаю, что имена индексных таблиц перевернуты. Мне тоже удалось это сделать в первый раз, и я поймал это прямо перед публикацией. - person TheJacobTaylor; 18.08.2009
comment
TheJacobTaylor был прав, я перевернул имена таблиц. Я исправил это. - person Jeff Siver; 18.08.2009
comment
У меня вопрос, почему мы оба получили их задом наперед? Я просмотрел пост еще раз, но не увидел триггера, который я сделал неправильно. - person TheJacobTaylor; 18.08.2009
comment
Я предполагаю: во введении вопроса они представлены как музыка, а затем прослушивание, но запросы на создание сначала показывают прослушивание. - person Rob Drimmie; 18.08.2009

По большей части вы также должны индексировать любой столбец, который используется в JOIN. В вашем случае вы должны индексировать как listentrack.url, так и music.url

@jeff s - Индекс music.date_created не поможет, потому что вы сначала запускаете его через функцию, поэтому MySQL не может использовать индекс для этого столбца. Часто вы можете переписать запрос так, чтобы индексированный ссылочный столбец использовался статически, например:

DATEDIFF(DATE(date_created),'2009-08-15') = 0

становится

date_created >= '2009-08-15' and date_created < '2009-08-15'

Это отфильтрует записи за 15 августа 2009 г. и позволит использовать любые индексы в этом столбце в качестве кандидатов. Обратите внимание, что MySQL может НЕ использовать этот индекс, это зависит от других факторов.

Лучше всего сделать двойной индекс для listentrack(url, date_created), а затем еще один индекс для music.url.

Эти 2 индекса будут охватывать этот конкретный запрос.

Обратите внимание, что если вы запустите EXPLAIN для этого запроса, вы все равно получите using filesort, потому что он должен записывать записи во временную таблицу на диск, чтобы выполнить ORDER BY.

В общем, вы всегда должны запускать свой запрос под EXPLAIN, чтобы получить представление о том, как MySQL будет выполнять запрос, а затем идти оттуда. См. документацию EXPLAIN:

http://dev.mysql.com/doc/refman/5.0/en/using-explain.html

person Cody Caughlan    schedule 18.08.2009
comment
Жестокий тайминг. :) Отличный ответ. Обгони меня на секунды. - person TheJacobTaylor; 18.08.2009
comment
Большое спасибо звучит очень полезно. я перейду по вашей ссылке, прочитаю и попробую ваш пример - person Khary; 18.08.2009

Попробуйте создать индекс, который поможет с соединением:

CREATE INDEX idx_url ON music (url);
person VoteyDisciple    schedule 18.08.2009

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


Я бы добавил новые индексы, как упоминают другие. В частности, я бы добавил: URL-адрес музыки listentrack date_created,url

Это значительно улучшит ваше присоединение.

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

Не уверен в синтаксисе навскидку: где '2009-08-15 00:00:00' ‹= date_created ‹ 2009-08-16 00:00:00

Это должно позволить ему быстро использовать индекс для поиска соответствующих записей. Объединенный двухключевой указатель по музыке должен позволять находить записи по дате и URL-адресу. Вы должны поэкспериментировать, возможно, им лучше пойти в другом направлении url, date_created в индексе.

План объяснения для этого запроса должен указывать «используя индекс» в правом столбце для обоих. Это означает, что ему не нужно будет обращаться к данным в таблице для расчета ваших сумм.

Я бы также проверил настройки памяти, которые вы настроили для MySQL. Похоже, у вас недостаточно выделенной памяти. Будьте очень осторожны с различиями между настройками на основе сервера и настройками на основе потока. Сервер с кешем 10 МБ довольно мал, поток с кешем 10 МБ может быстро использовать много памяти.

Джейкоб

person TheJacobTaylor    schedule 18.08.2009
comment
Я использую данные в этой таблице, но я отлаживал, откуда взялась медлительность. Нет запроса на присоединение 1 сек. Присоединяйтесь после добавления индекса idx на music.url за 7 секунд, а добавление music.plays увеличило его до 10. - person Khary; 18.08.2009
comment
Можете ли вы опубликовать план объяснения для реального запроса или для запроса с индексом? О каком количестве рекордов идет речь за сутки (на порядок)? Сколько памяти вы используете на коробке? Кэширование индексов и данных или просто кеширование в InnoDB имеют решающее значение и их легко исправить. - person TheJacobTaylor; 18.08.2009

Предварительная группировка и последующее объединение значительно ускоряют работу с MySQL/MyISAM. (Я подозреваю, что меньше этого требуется для других БД)

Это должно работать примерно так же быстро, как и версия без присоединения:

SELECT
   total, a.url, title
FROM
(
  SELECT COUNT(*) as total, url
  from listenTrack
  WHERE DATEDIFF(DATE(date_created),'2009-08-15') = 0
  GROUP BY url
  ORDER BY total DESC
  LIMIT 0,10
) as a
LEFT JOIN music ON music.url = a.url
;

P.S. - Сопоставление двух таблиц с идентификатором вместо URL-адреса является разумным советом.

person Community    schedule 15.09.2011

Почему вы повторяете URL-адрес в обеих таблицах?

Вместо этого пусть listentrack удерживает music_id и присоединяйтесь к нему. Избавляется от текстового поиска, а также от дополнительного индекса.

Кроме того, это, пожалуй, более правильно. Вы отслеживаете время прослушивания определенного трека, а не URL-адрес. Что делать, если URL-адрес изменится?

person kyoryu    schedule 18.08.2009

После того, как вы добавите индексы, вы можете попробовать добавить новый столбец для date_created, чтобы он был unix_timestamp, что ускорит математические операции.

Я не уверен, почему у вас есть функция diff, поскольку кажется, что вы ищете все строки, которые были обновлены в определенную дату.

Возможно, вы захотите посмотреть на свой запрос, поскольку он, похоже, содержит ошибку.

Если вы используете модульные тесты, вы можете сравнить результаты своего запроса и запроса, используя вместо этого временную метку unix.

person James Black    schedule 18.08.2009

вы можете добавить индекс в поле URL обеих таблиц.

сказав, что, когда я перешел с mysql на sql server 2008 с теми же запросами и теми же структурами базы данных, запросы выполнялись на 1-3 порядка быстрее.

я думаю, что некоторые из них были связаны с rdbms (оптимизаторы mysql не так хороши ...), а некоторые из них могли быть связаны с тем, как rdbms резервирует системные ресурсы. хотя сравнения проводились на производственных системах, где работала только БД.

person mson    schedule 18.08.2009

Это ниже, вероятно, поможет ускорить запрос.

СОЗДАЙТЕ ИНДЕКС music_url_index ON музыки (url), ИСПОЛЬЗУЯ BTREE; СОЗДАЙТЕ ИНДЕКС listenTrack_url_index ON listenTrack (url), ИСПОЛЬЗУЯ BTREE;

Вам действительно нужно знать общее количество сравнений и просмотров строк, которые происходят. Чтобы получить этот ответ, посмотрите на код здесь, как это сделать, используя объяснение http://www.siteconsortium.com/h/p1.php?id=mysql002.

person JTHouseCat    schedule 05.02.2014