это может быть выполнено быстрее с большим количеством данных [MySQL]

0

есть ли способ оптимизации следующего запроса:

EXPLAIN EXTENDED SELECT keyword_id, ck.keyword, COUNT( article_id ) AS cnt
FROM career_article_keyword
LEFT JOIN career_keywords ck
USING ( keyword_id ) 
WHERE keyword_id
IN (

SELECT keyword_id
FROM career_article_keyword
LEFT JOIN career_keywords ck
USING ( keyword_id ) 
WHERE article_id
IN (

SELECT article_id
FROM career_article_keyword
WHERE keyword_id =9
)
AND keyword_id <>9
)
GROUP BY keyword_id
ORDER BY cnt DESC

Основная задача здесь, если у меня есть определенное keyword_id (CURRENT_KID), мне нужно найти все ключевые слова, которые когда-либо принадлежали какой-либо статье вместе с CURRENT_KID, и результат сортировки основан на количестве использования этих ключевых слов

таблицы, определенные как:

mysql> show create table career_article_keyword;
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table                  | Create Table                                                                                                                                                                                                                                                                                                                                               |
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| career_article_keyword | CREATE TABLE `career_article_keyword` (
  `article_id` int(11) unsigned NOT NULL,
  `keyword_id` int(11) NOT NULL,
  UNIQUE KEY `article_id` (`article_id`,`keyword_id`),
  CONSTRAINT `career_article_keyword_ibfk_1` FOREIGN KEY (`article_id`) REFERENCES `career` (`menu_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> show create table career_keywords;
+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table           | Create Table                                                                                                                                                                                                         |
+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| career_keywords | CREATE TABLE `career_keywords` (
  `keyword_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `keyword` varchar(250) NOT NULL,
  PRIMARY KEY (`keyword_id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 |
+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

вывод "объяснения" пугает меня

http://o7.no/J6ThIs

на больших данных этот запрос может убить все:) Могу ли я сделать это быстрее?

спасибо.

Теги:
optimization
sql-optimization
high-load

1 ответ

2
Лучший ответ

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

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

Теперь перетащите запрос поэтапно:

  • Извлеките "любую статью, содержащую определенное заданное ключевое слово" (не беспокоясь о дубликатах):

    SELECT ca2.article_id
    FROM
           career_article_keyword AS ca2
    WHERE
          ca2.keyword_id = 9;
    
  • Извлеките "все другие ключевые слова, которые появляются в [выше]"

    SELECT ca1.keyword_id
    FROM
           career_article_keyword AS ca1
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ca1.keyword_id;
    
  • Извлеките "[выше] вместе со счетом всех статей, в которых отображаются эти ключевые слова:

    SELECT ca1.keyword_id, COUNT(DISTINCT ca0.article_id) AS cnt
    FROM
           career_article_keyword AS ca0
      JOIN career_article_keyword AS ca1 USING (keyword_id)
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ca1.keyword_id
    ORDER BY cnt DESC;
    
  • Наконец, мы хотим добавить к выходу соответствующее ключевое слово непосредственно из таблицы career_keyword:

    SELECT ck.keyword_id, ck.keyword, COUNT(DISTINCT ca0.article_id) AS cnt
    FROM
           career_keywords        AS ck 
      JOIN career_article_keyword AS ca0 USING (keyword_id)
      JOIN career_article_keyword AS ca1 USING (keyword_id)
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ck.keyword_id -- equal to ca1.keyword_id due to join conditions
    ORDER BY cnt DESC;
    

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

Оглядываясь назад на этот запрос, мы видим, что соединения выполняются в следующих столбцах:

  • career_keywords.keyword_id в ck JOIN ca0

    В этой таблице указан PRIMARY KEY (`keyword_id`), поэтому есть хороший индекс, который можно использовать для этого объединения.

  • career_article_keyword.article_id в ca1 JOIN ca2

    В этой таблице указано UNIQUE KEY `article_id` (`article_id`,`keyword_id`), и поскольку article_id является самым левым столбцом в этом индексе, есть хороший индекс, который может быть использован для этого объединения.

  • career_article_keyword.keyword_id в ck JOIN ca0 и ca0 JOIN ca1

    Нет индекса, который может быть использован для этого объединения: единственный индекс, определенный в этой таблице, имеет другой столбец, article_id слева от keyword_id - поэтому MySQL не может найти записи keyword_id в индексе без первого зная article_id. Я предлагаю вам создать новый индекс, который имеет keyword_id в качестве его самого левого столбца.

    (Необходимость этого индекса могла быть одинаково проверена непосредственно из вашего исходного запроса, где ваши два внешних запроса выполняют объединения в этом столбце.)

  • 0
    к сожалению, ваш запрос восстановил неверные данные :( мои и ваши версии вы можете посмотреть здесь o7.no/IFXd5m
  • 0
    @ user1016265: исправленная версия выше должна быть правильной. Вы обязательно career_article_keyword присоединяете таблицу career_article_keyword к себе (найдите статьи, содержащие ключевое слово, найдите все другие ключевые слова в этих статьях, посчитайте все статьи, содержащие эти ключевые слова), поэтому, безусловно, важно, если эта таблица станет большой; Вы пробовали сравнить свой запрос с моим пересмотренным (после создания нового индекса, который я предлагаю)?
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню