Коли використовувати STRAIGHT_JOIN з MySQL


88

У мене просто був досить складний запит, з яким я працював, і його запуск займав 8 секунд. EXPLAIN показував дивний порядок таблиць, і всі мої індекси не використовувались навіть із підказкою FORCE INDEX. Я натрапив на ключове слово STRAIGHT_JOIN join і почав замінювати ним деякі свої ВНУТРІШНІ ПРИЄДНАННЯ. Я помітив значне покращення швидкості. Врешті-решт я просто замінив усі свої ключові слова INNER JOIN на STRAIGHT_JOIN для цього запиту, і тепер він працює за 0,01 секунди.

Моє питання: коли ви використовуєте STRAIGHT_JOIN, а коли INNER JOIN? Чи є причина не використовувати STRAIGHT_JOIN, якщо ви пишете хороші запити?

Відповіді:


73

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

Я рекомендую залишити всі запити як звичайні ПРИЄДНАННЯ. Якщо ви виявите, що в одному запиті використовується неоптимальний план запитів, я пропоную спочатку спробувати трохи переписати або реструктурувати запит, щоб побачити, чи оптимізатор потім вибере кращий план запиту. Крім того, принаймні для innodb переконайтеся, що справа не лише в тому, що статистика вашого індексу застаріла ( ТАБЛИЦЯ АНАЛІЗУ ). Це може змусити оптимізатор вибрати поганий план запитів. Підказки щодо оптимізатора, як правило, повинні бути вашим останнім засобом.

Ще однією причиною не використовувати підказки щодо запитів є те, що розподіл даних може змінюватися з часом, або вибірковість індексу може змінюватися тощо, коли ваша таблиця росте. Ваші підказки щодо оптимального запиту з часом можуть стати неоптимальними. Але оптимізатор не зможе адаптувати план запиту через ваші застарілі підказки. Ви залишаєтесь більш гнучкими, якщо дозволяєте оптимізатору приймати рішення.


59
Ця відповідь насправді не пояснює, коли використовувати straight_join .
Pacerier

23

З посилання MySQL JOIN :

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


28
Дякую, але я вже читав посібник MySQL на ньому. Сподіваючись на подальші пояснення.
Грег

20

Ось сценарій, який нещодавно з’явився на роботі.

Розглянемо три таблиці, A, B, C.

А має 3000 рядків; B має 300 000 000 рядків; а С має 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 настійно воліє використовувати індекси для будь-яких фільтрів і об'єднує над сортами; це має сенс, оскільки більшість людей не намагаються сортувати всю базу даних, а мають обмежену підмножину рядків, що реагують на запит, і сортування обмеженої підмножини набагато швидше, ніж фільтрація цілої таблиці, незалежно від того, сортується вона чи ні. У цьому випадку, ставлячи пряме приєднання відразу після таблиці, яка мала індексований стовпець, я хотів відсортувати за виправленими речами.


Як би ви використали пряме приєднання для вирішення проблеми?
Ганнеле

@Hannele straight_joinоцінює ліву таблицю перед правою. Отже, якщо ви хочете перейти з A -> B -> Cмого прикладу, перше joinключове слово можна замінити на straight_join.
Barry Kelly

Ах, акуратно. Було б корисно включити це як приклад у свою відповідь :)
Ганнеле

18

MySQL не обов'язково добре вибирає порядок об'єднання в складних запитах. Вказуючи складний запит як straight_join, запит виконує об’єднання в тому порядку, який вони вказали. Помістивши таблицю спочатку найменшим спільним знаменником та вказавши straight_join, ви зможете покращити ефективність запиту.


11

STRAIGHT_JOIN, використовуючи цей пункт, ви можете контролювати JOINпорядок: яка таблиця сканується у зовнішньому циклі, а яка - у внутрішньому.


Що таке зовнішня петля та внутрішня петля?
Істіаке Ахмед

Таблиці @IstiaqueAhmed об'єднуються вкладеними циклами (візьміть перший рядок із таблиці A, а таблицю перекидання циклу B, потім візьміть другий рядок ... і так далі. Тут таблиця A знаходиться у зовнішньому циклі)
Бухгалтер,

6

Я скажу вам, чому мені довелося використовувати STRAIGHT_JOIN:

  • У мене була проблема продуктивності із запитом.
  • Спрощуючи запит, запит був раптово ефективнішим
  • Намагаючись зрозуміти, яка саме частина спричиняє проблему, я просто не зміг. (2 ліві об'єднання разом були повільними, і кожен з них був незалежно швидким)
  • Потім я виконав EXPLAIN як із повільним, так і з швидким запитом (додайте одне з лівих об’єднань)
  • Дивно, але MySQL повністю змінив замовлення JOIN між двома запитами.

Тому я змусив одне з об'єднань отримати straight_join, щоб ПРИМУКАТИ попереднє з'єднання прочитати першим. Це завадило MySQL змінити порядок виконання і працювало як шарм!


2

З мого короткого досвіду, одна із ситуацій, 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

1
(Я прикрасив вашу відповідь.)
Рік Джеймс,

2

Якщо кінці запиту з 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 "київ", не тягнучи їх у тимчасовій таблиці.
  • оскільки підзапит, за визначенням, не упорядкований, його ORDER BYпотрібно повторити у зовнішньому запиті. (Оптимізатор може знайти спосіб уникнути фактичного здійснення іншого сортування.)
  • Так, це безладніше. Але зазвичай це швидше.

Я проти використання хітів, бо "Навіть якщо сьогодні це швидше, завтра воно може бути швидшим".


0

Я знаю, що це трохи давно, але ось сценарій, я робив пакетний сценарій для заповнення певної таблиці. У якийсь момент запит виконувався дуже повільно. Здається, порядок приєднання був неправильним для певних записів:

  • У правильному порядку

введіть тут опис зображення

  • Збільшення ідентифікатора на 1 псує порядок. Зверніть увагу на поле «Додатково»

введіть тут опис зображення

  • Використання straight_join вирішує проблему

введіть тут опис зображення

Неправильний порядок виконується приблизно 65 секунд при використанні прямого_приєднання за мілісекунди


-5
--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

3
Це не дає вам майже достатньо інформації, щоб зрозуміти, коли прямі приєднання є доречними.
Ганнеле
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.