Когда использовать STRAIGHT_JOIN с MySQL - PullRequest
83 голосов
/ 04 февраля 2009

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

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

Ответы [ 9 ]

70 голосов
/ 05 февраля 2009

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

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

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

22 голосов
/ 05 февраля 2009

С Ссылка на MySQL JOIN :

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

18 голосов
/ 04 февраля 2009

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

16 голосов
/ 09 февраля 2017

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

Рассмотрим три таблицы, 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 настоятельно предпочитает использовать индексы для любых фильтров и объединяет их по типам; это имеет смысл, потому что большинство людей не пытаются отсортировать всю базу данных, а имеют ограниченное подмножество строк, которые реагируют на запрос, и сортировка ограниченного подмножества выполняется намного быстрее, чем фильтрация всей таблицы, независимо от того, отсортирована она или нет. не. В этом случае прямое соединение сразу после таблицы, в которой был индексированный столбец, я хотел отсортировать по фиксированным вещам.

11 голосов
/ 13 апреля 2012

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

4 голосов
/ 10 ноября 2017

Я скажу вам, почему я должен был использовать STRAIGHT_JOIN:

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

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

1 голос
/ 27 апреля 2018

Если ваш запрос заканчивается на 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 позволяет получить только 30 "whatevers" без перемещения их во временной таблице.
  • , поскольку подзапрос по определению неупорядочен, ORDER BY необходимо повторить.
  • Да, это грязнее. Но обычно это быстрее.

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

1 голос
/ 17 апреля 2018

По моему короткому опыту, одна из ситуаций, когда 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 первый , это вызовет Using index; Using temporary; Using filesort, потому что

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

источник

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

sales STRAIGHT_JOIN stores
0 голосов
/ 05 апреля 2017
--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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...