У меня был довольно сложный запрос, с которым я работал, и его выполнение занимало 8 секунд. EXPLAIN показывал странный порядок таблиц, и не все мои индексы использовались даже с подсказкой FORCE INDEX. Я наткнулся на ключевое слово соединения STRAIGHT_JOIN и начал заменять им некоторые из моих ключевых слов INNER JOIN. Заметил значительное улучшение скорости. В конце концов я просто заменил все свои ключевые слова INNER JOIN на STRAIGHT_JOIN для этого запроса, и теперь он выполняется за 0,01 секунды.
У меня вопрос: когда вы используете STRAIGHT_JOIN, а когда - INNER JOIN? Есть ли причина не использовать STRAIGHT_JOIN, если вы пишете хорошие запросы?
straight_join
.Из ссылки MySQL JOIN :
«STRAIGHT_JOIN аналогичен JOIN, за исключением того, что левая таблица всегда читается перед правой таблицей. Это может использоваться для тех (немногих) случаев, когда оптимизатор соединения размещает таблицы в неправильном порядке».
источник
Вот сценарий, который недавно возник на работе.
Рассмотрим три таблицы: A, B, C.
A имеет 3000 строк; B имеет 300000000 строк; а в 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 настоятельно предпочитает использовать индексы для любых фильтров и объединений вместо сортировок; это имеет смысл, потому что большинство людей не пытаются отсортировать всю базу данных, а имеют ограниченное подмножество строк, которые реагируют на запрос, а сортировка ограниченного подмножества происходит намного быстрее, чем фильтрация всей таблицы, независимо от того, отсортирована она или не. В этом случае, помещая прямое соединение сразу после таблицы, в которой был индексированный столбец, я хотел отсортировать фиксированные элементы.источник
straight_join
оценивает левую таблицу перед правой. Итак, если вы хотите продолжитьA -> B -> C
в моем примере, первоеjoin
ключевое слово можно заменить наstraight_join
.MySQL не обязательно хорош в выборе порядка соединения в сложных запросах. При указании сложного запроса как direct_join запрос выполняет объединения в том порядке, в котором они указаны. Поместив в первую очередь таблицу с наименьшим общим знаменателем и указав direct_join, вы сможете повысить производительность запроса.
источник
STRAIGHT_JOIN
с помощью этого предложения вы можете управлятьJOIN
порядком: какая таблица сканируется во внешнем цикле, а какая во внутреннем.источник
Я расскажу, почему мне пришлось использовать STRAIGHT_JOIN:
Поэтому я заставил одно из соединений быть direct_join, чтобы заставить предыдущее соединение быть прочитанным первым. Это помешало MySQL изменить порядок выполнения и работало как шарм!
источник
По моему короткому опыту, одна из ситуаций,
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
потому чтоисточник
здесь оптимизатору нужна небольшая помощь, сказав ему
sales
сначала ударить, используяисточник
Если концы запроса с
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;
Примечания:
INDEX(date, id)
.sales
позволяет получить только 50 «чего угодно», не таща их за временным столом.ORDER BY
во внешнем запросе должен быть повторен. (Оптимизатор может найти способ избежать выполнения другой сортировки.)Я против использования хитов, потому что «даже если сегодня это быстрее, завтра может не быть».
источник
Я знаю, что это немного устарело, но вот сценарий, я делал пакетный скрипт для заполнения определенной таблицы. В какой-то момент запрос выполнялся очень медленно. Похоже, что порядок объединения для определенных записей был неправильным:
Неправильный порядок выполняется примерно 65 секунд, а при использовании Straight_join выполняется за миллисекунды.
источник
--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
источник