Оптимизация запроса MySQL с несколькими левыми соединениями

У меня есть таблицы NewsStories, которые я оставил с некоторыми связанными таблицами. Каждая новость может содержать несколько изображений, категорий и адресов. Таким образом, запрос по существу:

SELECT * FROM NewStories 

LEFT JOIN Images ON Newstories.id=Images.story_id

LEFT JOIN Categories ON NewsStories.id=Categories.story_id

LEFT JOIN Addresses ON NewsStories.id=Addresses.story_id

WHERE ...

Как правило, несколько изображений и адресов для каждой истории и 1 или 2 категории. Таблица NewsStories содержит около 10 000 статей.

Проблема в том, что производительность довольно медленная (порядка 15-20 секунд, хотя она немного меняется и иногда падает до 5 секунд).

Мне было интересно, есть ли лучший способ организовать запрос для его ускорения (я довольно новичок в SQL).

В частности, кажется довольно расточительным, что количество строк для данной истории умножается на количество изображений, умноженное на количество адресов, умноженное на количество категорий.

Я по существу пытаюсь восстановить свойства новостной истории в один объект, с которым я могу манипулировать в интерфейсе.

Здесь объяснение (извинения, если форматирование не получается правильно). Я предполагаю, что я не индексирую адреса правильно, если это "Использование где". Это правильно?

id  select_type table   type    possible_keys   key key_len ref rows    Extra

1   SIMPLE  Addresses   ALL NULL    NULL    NULL    NULL    6640    Using where

1   SIMPLE  NewsStories eq_ref  PRIMARY PRIMARY 767 NewsStories.Addresses.story_id 1    Using where

1   SIMPLE  Images  ref PRIMARY PRIMARY 767 NewsStories.NewsStories.id  1   Using index

1   SIMPLE  Categories  ref PRIMARY PRIMARY 767 NewsStories.NewStories.id   1

Ответ 1

  • Убедитесь, что у вас есть индексы в полях, которые содержатся в ваших операторах WHERE и условиях ON, первичные ключи индексируются по умолчанию, но вы также можете создавать индексы вручную, если вам нужно.

CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name     [Index_type]     ON tbl_name (index_col_name,...)     [Index_type]

index_col_name:     col_name [(длина)] [ASC | DESC]

index_type:     ИСПОЛЬЗОВАНИЕ {BTREE | HASH}

  • Проверьте, действительно ли вам нужно выбрать каждый столбец во всех таблицах? Если нет, убедитесь, что вы выбрали только нужные столбцы, не используйте select *

  • Двойная проверка, действительно ли вам нужны ЛЕВОЕ СОЕДИНЕНИЕ, если нет, используйте INNER JOINs.

  • Если производительность по-прежнему остается проблемой после того, как вы выполнили настройку своего запроса, рассмотрите возможность денормализации вашей схемы для устранения объединений

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

  • Проверьте, что ни одно из ваших соединений не относится к представлениям, а не к фактическим таблицам.

ссылки:

http://www.sphinxsearch.com

http://dev.mysql.com/doc/refman/5.0/en/create-index.html

Ответ 2

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

Попробуйте объяснить запрос (EXPLAIN SELECT * FROM ...etc) в консоли MySQL, чтобы увидеть, как MySQL обрабатывает таблицы внутри. Вставьте результаты в свой вопрос для получения дополнительной справки.