Когда использовать STRAIGHT_JOIN с MySQL

У меня был довольно сложный запрос, с которым я работал, и для его выполнения потребовалось 8 секунд. EXPLAIN показывал странный порядок таблиц, и мои индексы не все использовались даже с подсказкой FORCE INDEX. Я наткнулся на ключевое слово STRAIGHT_JOIN join и начал заменять некоторые из моих ключевых слов INNER JOIN. Я заметил значительное улучшение скорости. В конце концов я просто заменил все свои ключевые слова INNER JOIN на STRAIGHT_JOIN для этого запроса, и теперь он работает в 0,01 секунды.

Мой вопрос в том, когда вы используете STRAIGHT_JOIN и когда вы используете INNER JOIN? Есть ли причина не использовать STRAIGHT_JOIN, если вы пишете хорошие запросы?

Ответ 1

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

Моя рекомендация - оставить все запросы в качестве обычных JOIN. Если вы обнаружите, что в одном запросе используется неоптимальный план запроса, я бы предложил сначала попытаться переписать или переструктурировать запрос немного, чтобы определить, будет ли оптимизатор выбрать лучший план запроса. Кроме того, по крайней мере, для innodb убедитесь, что это не только статистика вашего индекса устарела (ANALYZE TABLE). Это может заставить оптимизатора выбрать плохой план запроса. Рекомендации оптимизатора обычно должны быть вашим последним средством.

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

Ответ 2

От ссылка MySQL JOIN:

"STRAIGHT_JOIN похож на JOIN, за исключением того, что левая таблица всегда считывается перед правой таблицей. Это может быть использовано для тех (нескольких) случаев, для которых оптимизатор соединения помещает таблицы в неправильном порядке".

Ответ 3

MySQL не обязательно хорош в выборе порядка объединения в сложных запросах. Задав сложный запрос как straight_join, запрос выполняет объединения в указанном порядке. Поместив таблицу в наименьший общий знаменатель и указав straight_join, вы сможете улучшить производительность запросов.

Ответ 4

Вот сценарий, который появился совсем недавно на работе.

Рассмотрим три таблицы: A, B, C.

А имеет 3000 строк; B имеет 300 000 000 строк; и C имеет 2000 строк.

Внешние ключи определены: B (a_id), B (c_id).

Предположим, у вас есть запрос, который выглядит следующим образом:

select a.id, c.id
from a
join b on b.a_id = a.id
join c on c.id = b.c_id

По моему опыту, в этом случае MySQL может выбрать C → B → A. C меньше, чем A, а B огромен, и все они равносильны.

Проблема в том, что MySQL не обязательно учитывает размер пересечения между (C.id и B.c_id) и (A.id и B.a_id). Если соединение между B и C возвращает столько же строк, сколько B, тогда это очень плохой выбор; если бы начало с A отфильтровывало бы B на столько строк, сколько A, тогда это был бы гораздо лучший выбор. straight_join можно использовать для наведения порядка следующим образом:

select a.id, c.id
from a
straight_join b on b.a_id = a.id
join c on c.id = b.c_id

Теперь a должен быть включен до b.

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

Это зависит от характеристик, хотя. Если распределение данных изменяется, расчет может измениться. Это также зависит от деталей реализации механизма соединения.

Наихудшие случаи, которые я видел для MySQL, когда почти требуется straight_join или агрессивная подсказка индекса, - это запросы, которые разбивают на страницы большого количества данных в строгом порядке сортировки с легкой фильтрацией. MySQL настоятельно предпочитает использовать индексы для любых фильтров и объединяет их по типам; это имеет смысл, потому что большинство людей не пытаются отсортировать всю базу данных, а имеют ограниченное подмножество строк, которые реагируют на запрос, и сортировка ограниченного подмножества выполняется намного быстрее, чем фильтрация всей таблицы, независимо от того, отсортирована она или нет. не. В этом случае прямое соединение сразу после таблицы с индексированным столбцом я хотел отсортировать по фиксированным вещам.

Ответ 5

STRAIGHT_JOIN, используя этот раздел, вы можете управлять порядком JOIN: какая таблица сканируется во внешнем цикле и какая из них находится во внутреннем цикле.

Ответ 6

Я расскажу вам, почему мне пришлось использовать STRAIGHT_JOIN:

  • У меня была проблема с производительностью с запросом.
  • Упрощение запроса, запрос был более эффективен
  • Попытка выяснить, какая конкретная часть приносит проблему, я просто не мог. (2 левых соединения вместе были медленными, и каждый из них был независимо быстрым)
  • Затем я выполнил EXPLAIN с медленным и быстрым запросом (добавьте одно из левых соединений)
  • Удивительно, но MySQL полностью изменил заказы JOIN между двумя запросами.

Поэтому я заставил одно из соединений быть straight_join, чтобы FORCE предыдущее соединение было прочитано первым. Это помешало MySQL изменить порядок выполнения и работать как шарм!

Ответ 7

Если ваш запрос заканчивается на ORDER BY... LIMIT..., может оказаться оптимальным переформулировать запрос, чтобы заставить оптимизатор выполнить LIMIT перед JOIN.

(Этот ответ не относится только к первоначальному вопросу о STRAIGHT_JOIN и не относится ко всем случаям STRAIGHT_JOIN.)

Начиная с примера @Accountant م, в большинстве случаев это должно выполняться быстрее. (И это избегает необходимости подсказок.)

SELECT  whatever
    FROM  ( SELECT id FROM sales
                ORDER BY  date, id
                LIMIT  50
          ) AS x
    JOIN  sales   ON sales.id = x.id
    JOIN  stores  ON sales.storeId = stores.id
    ORDER BY  sales.date, sales.id;

Примечание:

  • Сначала выбирается 50 идентификаторов. Это будет особенно быстро с INDEX(date, id).
  • Затем соединение с sales позволяет вам получить только 50 "whatevers", не таща их во временной таблице.
  • поскольку подзапрос по определению неупорядочен, ORDER BY должен повторяться во внешнем запросе. (Оптимизатор может найти способ на самом деле избежать другого рода.)
  • Да, это грязнее. Но обычно это быстрее.

Я против использования хитов, потому что "даже если сегодня будет быстрее, завтра может не получиться".

Ответ 8

Из моего короткого опыта одна из ситуаций, когда STRAIGHT_JOIN сократил мой запрос с 30 секунд до 100 миллисекунд, заключается в том, что первая таблица в плане выполнения не была таблицей с порядком по столбцам

-- table sales (45000000) rows
-- table stores (3) rows
SELECT whatever
FROM 
    sales 
    INNER JOIN stores ON sales.storeId = stores.id
ORDER BY sales.date, sales.id 
LIMIT 50;
-- there is an index on (date, id)

Если оптимизатор решит сначала нажать stores first, это вызовет Using index; Using temporary; Using filesort, потому что

если ORDER BY или GROUP BY содержит столбцы из таблиц, отличных от первая таблица в очереди соединения, создается временная таблица.

источник

здесь оптимизатору нужна небольшая помощь, сказав ему сначала нажать sales, используя

sales STRAIGHT_JOIN stores

Ответ 9

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

  • В правильном порядке

enter image description here

  • Увеличение идентификатора на 1 портит порядок. Обратите внимание на поле "Extra"

enter image description here

  • Использование Straight_join устраняет проблему

enter image description here

Неверный порядок выполняется в течение около 65 секунд, а прямое соединение выполняется в миллисекундах.

Ответ 10

--use 120s, 18 million data
    explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d, tvassist_taid_all t
    WHERE d.taid = t.taid
      AND t.client_version >= '21004007'
      AND t.utdid IS NOT NULL
      AND d.recommend_day = '20170403'
    LIMIT 0, 10000

--use 3.6s repalce by straight join
 explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d
    STRAIGHT_JOIN 
      tvassist_taid_all t on d.taid = t.taid 
    WHERE 
     t.client_version >= '21004007'
       AND d.recommend_day = '20170403'

      AND t.utdid IS NOT NULL  
    LIMIT 0, 10000