Оптимизация простого запроса, выполнение которого занимает 1 минуту - PullRequest
0 голосов
/ 09 декабря 2010

У меня довольно простой SQL-запрос, но его выполнение занимает почти минуту:

SELECT
 i.id,
 ...,
 a.id AS albums_id,
 ...,
 u.id AS users_id,
 ...
FROM
 images i
  LEFT JOIN albums a ON i.albums_id = a.id
  LEFT JOIN users u ON a.users_id = u.id
WHERE
 a.access = 'public'
 AND i.num_of_views > 0
ORDER BY
 i.num_of_views DESC
LIMIT
 0, 60

Результат EXPLAIN для вышеуказанного запроса:

http://i.min.us/ibeAnM.png

В таблицах участвуют:

images (~ 4 822 000 строк), albums (~ 149 000 строк), users (~ 43 000 строк)

Соответствующие индексы:

albums: доступ (access, num_of_images, album_time), access_2 (access, num_of_images, num_of_all_comments, album_time), users_id (users_id, album_time)

images: browser_2 (num_of_views), album_id (album_id, image_order)

Все таблицы InnoDB, работающие на MySql v5.1.47

Так, как мне уменьшить это до секунды?

Пожалуйста, оставьте комментарий, если вам нужна дополнительная информация.

edit: users таблица может быть объединена либо с albums, либо images для меня не имеет значения.

edit2: перемещение a.access = 'public' из WHERE в JOIN действительно решает мою проблему, но возвращаемые результаты не верны (я получаю изображения из альбомов, которые не являются общедоступными), помещая a.access ... в оба WHERE и JOIN замедляет запрос еще больше, чем раньше.

Ответы [ 3 ]

3 голосов
/ 09 декабря 2010

Добавить индекс на albums.users_id.Я также согласен с комментариями относительно a.access = 'public'.Но индекс должен помочь в любом случае.

ОБНОВЛЕНИЕ

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

LEFT JOIN albums a ON (i.albums_id = a.id AND a.access = 'public')

ОБНОВЛЕНИЕ

На основе комментариев я бы удалил как можно больше LEFT JOINПоскольку я не уверен, что вам требуется в ваших результатах, я покажу это только для albums.Это не только уменьшит набор результатов, но и решит проблему применения фильтра.

JOIN albums a ON (i.albums_id = a.id AND a.access = 'public')
1 голос
/ 09 декабря 2010

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

Ян, если то, что вы пытаетесь задать в своемзапрос «Получить все изображения для всех общедоступных альбомов и пользователей этих альбомов», тогда вам не нужно левое соединение, вам нужно внутреннее соединение.При левом соединении будут возвращены все изображения для всех альбомов, но также будут возвращены все изображения, для которых нет соответствующего альбома.Вы можете добавить «and..ID NOT NULL», но это то же самое, что и INNER JOIN.

Я считаю, что вам нужно следующее:

SELECT
 i.id,
 ...,
 a.id AS albums_id,
 ...,
 u.id AS users_id,
 ...
FROM images i
  INNER JOIN albums a ON i.albums_id = a.id AND a.access = 'public'
  INNER JOIN users u ON a.users_id = u.id
WHERE i.num_of_views > 0
ORDER BY i.num_of_views DESC
LIMIT 0, 60

Если вы покинули альбомпользователям вы можете вернуть все альбомы, у которых нет пользователей.Не уверен, какой вы хотите.

1 голос
/ 09 декабря 2010

Исходя из ваших последних комментариев, вы должны использовать INNER JOIN для альбомов вместо LEFT JOIN.

SELECT
 i.id,
 ...,
 a.id AS albums_id,
 ...,
 u.id AS users_id,
 ...
FROM images i
  INNER JOIN albums a ON i.albums_id = a.id
  LEFT JOIN users u ON a.users_id = u.id
WHERE a.access = 'public'
 AND i.num_of_views > 0
ORDER BY i.num_of_views DESC
LIMIT 0, 60
...