Как оптимизировать запрос, ища строки, в которых строки условного соединения не существуют? - PullRequest
0 голосов
/ 13 февраля 2009

У меня есть таблица ключевых слов, которую я регулярно обновляю для API удаленного поиска, и у меня есть другая таблица, которая получает строку каждый раз, когда я обновляю одно из ключевых слов. Я использую эту таблицу, чтобы не допустить наступления друг на друга нескольких процессов и обновления одного и того же ключевого слова, а также сбора статистики. Поэтому, когда я раскручиваю свою программу, она запрашивает все ключевые слова, которые не обрабатывают запрос в данный момент и не имеют успешного запроса в течение последних 15 минут или любого другого интервала. Некоторое время все работало нормально, но теперь в таблице keyword_requests содержится почти 2 миллиона строк, и дела идут плохо. У меня есть индексы почти для каждого столбца в таблице Keywords_requests, но безрезультатно.

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

# Query_time: 20 Lock_time: 0 Rows_sent: 568 Rows_examined: 1826718

SELECT Keyword.id, Keyword.keyword
FROM `keywords` as Keyword
LEFT JOIN `keywords_requests` as KeywordsRequest
ON (
  KeywordsRequest.keyword_id = Keyword.id
  AND (KeywordsRequest.status = 'success' OR KeywordsRequest.status = 'active')
  AND KeywordsRequest.source_id = '29'
  AND KeywordsRequest.created > FROM_UNIXTIME(1234551323)
)
WHERE KeywordsRequest.id IS NULL
GROUP BY Keyword.id
ORDER BY KeywordsRequest.created ASC;

Ответы [ 4 ]

2 голосов
/ 13 февраля 2009

Кажется, ваш самый избирательный индекс на Keywords равен одному на KeywordRequest.created.

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

SELECT Keyword.id, Keyword.keyword
FROM `keywords` as Keyword
LEFT OUTER JOIN (
  SELECT *
  FROM `keywords_requests` as kr
  WHERE created > FROM_UNIXTIME(1234567890) /* Happy unix_time! */
) AS KeywordsRequest
ON (
  KeywordsRequest.keyword_id = Keyword.id
  AND (KeywordsRequest.status = 'success' OR KeywordsRequest.status = 'active')
  AND KeywordsRequest.source_id = '29'
)
WHERE keyword_id IS NULL;

Он (надеюсь) объединит два не очень больших источника.

И Билл Карвин прав, вам не нужны GROUP BY или ORDER BY

Нет точного контроля над планами в MySQL, но вы можете попробовать (попытаться) улучшить свой запрос следующими способами:

  1. Создайте составной индекс на (keyword_id, status, source_id, created) и сделайте так:

    SELECT Keyword.id, Keyword.keyword
    FROM `keywords` as Keyword
    LEFT OUTER JOIN `keywords_requests` kr
    ON (
      keyword_id = id
      AND status = 'success'
      AND source_id = '29'
      AND created > FROM_UNIXTIME(1234567890)
    )
    WHERE keyword_id IS NULL
    UNION
    SELECT Keyword.id, Keyword.keyword
    FROM `keywords` as Keyword
    LEFT OUTER JOIN `keywords_requests` kr
    ON (
      keyword_id = id
      AND status = 'active'
      AND source_id = '29'
      AND created > FROM_UNIXTIME(1234567890)
    )
    WHERE keyword_id IS NULL
    

    В идеале для индекса нужно использовать NESTED LOOPS.

  2. Создайте составной индекс для (status, source_id, created) и сделайте так:

    SELECT Keyword.id, Keyword.keyword
    FROM `keywords` as Keyword
    LEFT OUTER JOIN (
      SELECT *
      FROM `keywords_requests` kr
      WHERE
        status = 'success'
        AND source_id = '29'
        AND created > FROM_UNIXTIME(1234567890)
      UNION ALL
      SELECT *
      FROM `keywords_requests` kr
      WHERE
        status = 'active'
        AND source_id = '29'
        AND created > FROM_UNIXTIME(1234567890)
    )
    ON keyword_id = id
    WHERE keyword_id IS NULL
    

    Мы надеемся использовать HASH JOIN для еще более ограниченной хеш-таблицы.

1 голос
/ 13 февраля 2009

При диагностике производительности запросов MySQL одной из первых вещей, которые вам нужно проанализировать, является отчет от EXPLAIN .

Если вы научитесь читать информацию, которую EXPLAIN дает вам, вы сможете увидеть, где запросы не используют индексы, или где они вызывают дорогие сортировки файлов или другие красные флажки производительности.

Я заметил, что в вашем запросе GROUP BY не имеет значения, поскольку из KeywordRequests будет возвращена только одна пустая строка. Также ORDER BY не имеет значения, так как вы упорядочиваете столбец, который всегда будет NULL из-за вашего условия WHERE. Если вы удалите эти пункты, вы, вероятно, удалите сортировку файлов.

Также рассмотрите возможность переписать запрос в другие формы и измерить производительность каждой из них. Например:

SELECT k.id, k.keyword
FROM `keywords` AS k
WHERE NOT EXISTS (
  SELECT * FROM `keywords_requests` AS kr
  WHERE kr.keyword_id = k.id
   AND kr.status IN ('success', 'active')
   AND kr.source_id = '29'
   AND kr.created > FROM_UNIXTIME(1234551323)
);

Другие советы:

  • Является ли kr.source_id целым числом? Если это так, сравните с целым числом 29 вместо строки «29».
  • Есть ли соответствующие индексы на keyword_id, status, source_id, created? Возможно, лучше использовать даже составной индекс по всем четырем столбцам, поскольку MySQL будет использовать только один индекс на таблицу в данном запросе.

Вы сделали скриншот своего EXPLAIN вывода и разместили ссылку в комментариях. Я вижу, что запросом является , а не с использованием индекса из ключевых слов, что имеет смысл, так как в любом случае вы сканируете каждую строку в этой таблице. Фраза «Не существует» означает, что MySQL немного оптимизировал LEFT OUTER JOIN.

Я думаю, что это должно быть улучшено по сравнению с исходным запросом. Вероятно, GROUP BY / ORDER BY заставлял его сохранять промежуточный набор данных как временную таблицу и сортировать его на диск (что очень медленно!). То, что вы бы искали, это «Использование временного; использование файловой сортировки» в столбце «Дополнительные» информации EXPLAIN.

Так что, возможно, вы уже достаточно улучшили его, чтобы уменьшить узкое место.

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

CREATE INDEX kr_cover ON keywords_requests
  (keyword_id, created, source_id, status);

Вы можете дать MySQL подсказку для использования определенного индекса:

... FROM `keywords_requests` AS kr USE INDEX (kr_cover) WHERE ...
0 голосов
/ 04 августа 2009

Попробуйте это ВЫБЕРИТЕ Keyword.id, Keyword.keyword ОТ keywords как ключевое слово LEFT JOIN (выберите * из ключевых слов_requests, где source_id = «29» и (status = «success» ИЛИ status = «active») И source_id = '29' И создал> FROM_UNIXTIME (1234551323) И идентификатор равен NULL ) как ключевые слова ON ( KeywordsRequest.keyword_id = Keyword.id

) GROUP BY Keyword.id ORDER BY KeywordsRequest.created ASC;

0 голосов
/ 13 февраля 2009

Не знаю о MySQL, но в MSSQL я бы выбрал следующие линии атаки:

1) Создать покрывающий индекс по статусу KeywordsRequest, source_id и созданный

2) ОБЪЕДИНЯЙТЕ результаты, объединяющие ИЛИ на KeywordsRequest.status

3) Вместо NOT OISTS используйте o Outer Join (и попробуйте использовать UNION вместо OR)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...