web-dev-qa-db-fra.com

Quand utiliser STRAIGHT_JOIN avec MySQL

Je venais d'avoir une requête assez complexe avec laquelle je travaillais et cela prenait 8 secondes pour s'exécuter. EXPLAIN montrait un ordre de table étrange et mes index n'étaient pas tous utilisés, même avec l'indice FORCE INDEX. Je suis tombé sur le mot clé STRAIGHT_JOIN join et j'ai commencé à remplacer certains de mes mots clés INNER JOIN par celui-ci. J'ai remarqué une amélioration considérable de la vitesse. Finalement, je viens de remplacer tous mes mots clés INNER JOIN par STRAIGHT_JOIN pour cette requête et il s'exécute désormais en 0,01 seconde.

Ma question est de savoir quand utilisez-vous STRAIGHT_JOIN et quand utilisez-vous INNER JOIN? Y a-t-il une raison de ne pas utiliser STRAIGHT_JOIN si vous écrivez de bonnes requêtes?

83
Greg

Je ne recommanderais pas d'utiliser STRAIGHT_JOIN sans une bonne raison. Ma propre expérience est que l'optimiseur de requêtes MySQL choisit un plan de requête médiocre plus souvent que je ne le souhaiterais, mais pas assez souvent pour que vous le contourniez en général, ce que vous feriez si vous utilisiez toujours STRAIGHT_JOIN.

Ma recommandation est de laisser toutes les requêtes en tant que JOIN régulières. Si vous découvrez qu'une requête utilise un plan de requête sous-optimal, je suggère d'abord d'essayer de réécrire ou de restructurer un peu la requête pour voir si l'optimiseur choisira ensuite un meilleur plan de requête. De plus, pour innodb au moins, assurez-vous que ce n'est pas seulement que vos statistiques d'index sont obsolètes ( ANALYZE TABLE ). Cela peut amener l'optimiseur à choisir un mauvais plan de requête. Les conseils d'optimisation devraient généralement être votre dernier recours.

Une autre raison de ne pas utiliser d'indices de requête est que votre distribution de données peut changer au fil du temps, ou votre sélectivité d'index peut changer, etc. à mesure que votre table se développe. Vos indices de requête qui sont optimaux maintenant, peuvent devenir sous-optimaux au fil du temps. Mais l'optimiseur ne pourra pas adapter le plan de requête en raison de vos conseils désormais obsolètes. Vous restez plus flexible si vous laissez l'optimiseur prendre les décisions.

71
nathan

De référence MySQL JOIN :

"STRAIGHT_JOIN est similaire à JOIN, sauf que la table de gauche est toujours lue avant la table de droite. Cela peut être utilisé pour les (quelques) cas pour lesquels l'optimiseur de jointure place les tables dans le mauvais ordre."

22
jjclarkson

MySQL n'est pas nécessairement bon pour choisir l'ordre de jointure dans les requêtes complexes. En spécifiant une requête complexe en tant que jointure directe, la requête exécute les jointures dans l'ordre où elles sont spécifiées. En plaçant d'abord le tableau comme le dénominateur le moins commun et en spécifiant straight_join, vous pouvez améliorer les performances de la requête.

18
IAdapter

Voici un scénario qui est apparu récemment au travail.

Considérez trois tableaux, A, B, C.

A a 3 000 lignes; B a 300 000 000 lignes; et C a 2 000 lignes.

Les clés étrangères sont définies: B (a_id), B (c_id).

Supposons que vous ayez eu une requête qui ressemble à ceci:

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

D'après mon expérience, MySQL peut choisir d'aller C -> B -> A dans ce cas. C est plus petit que A et B est énorme, et ce sont tous des équijoins.

Le problème est que MySQL ne prend pas nécessairement en compte la taille de l'intersection entre (C.id et B.c_id) vs (A.id et B.a_id). Si la jointure entre B et C renvoie autant de lignes que B, alors c'est un très mauvais choix; si commencer par A aurait filtré B en autant de lignes que A, alors cela aurait été un bien meilleur choix. straight_join pourrait être utilisé pour forcer cet ordre comme ceci:

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

Maintenant, a doit être joint avant b.

En règle générale, vous souhaitez effectuer vos jointures dans un ordre qui minimise le nombre de lignes dans l'ensemble résultant. Donc, commencer avec une petite table et joindre de telle sorte que la jointure résultante sera également petite, est idéal. Les choses deviennent en forme de poire si vous commencez avec une petite table et que vous la joignez à une plus grande table finit tout aussi grande que la grande table.

Cela dépend des statistiques. Si la distribution des données change, le calcul peut changer. Cela dépend également des détails d'implémentation du mécanisme de jointure.

Les pires cas que j'ai vus pour MySQL qui ont presque requis straight_join ou l'indexation agressive d'index sont des requêtes qui paginent sur un grand nombre de données dans un ordre de tri strict avec un filtrage léger. MySQL préfère fortement utiliser les index pour tous les filtres et jointures plutôt que les tris; cela a du sens car la plupart des gens n'essaient pas de trier la base de données entière mais ont plutôt un sous-ensemble limité de lignes qui répondent à la requête, et le tri d'un sous-ensemble limité est beaucoup plus rapide que le filtrage de la table entière, qu'elle soit triée ou ne pas. Dans ce cas, en plaçant la jointure droite immédiatement après la table qui avait la colonne indexée, je voulais trier les choses fixes.

17
Barry Kelly

STRAIGHT_JOIN, en utilisant cette clause, vous pouvez contrôler l'ordre JOIN: quelle table est analysée dans la boucle externe et laquelle se trouve dans la boucle interne.

11
Mitendra

Je vais vous expliquer pourquoi j'ai dû utiliser STRAIGHT_JOIN:

  • J'ai eu un problème performance avec une requête.
  • Simplifiant la requête, la requête était soudainement plus efficace
  • Essayer de comprendre quelle partie spécifique posait le problème, je ne pouvais tout simplement pas. (2 assemblages gauches ensemble étaient lents et chacun était rapide indépendamment)
  • J'ai ensuite exécuté EXPLAIN avec une requête à la fois lente et rapide (ajouter une des jointures de gauche)
  • Étonnamment, MySQL a entièrement changé les commandes JOIN entre les 2 requêtes.

Par conséquent, j'ai forcé l'une des jointures à être straight_join pour FORCE la jointure précédente à lire en premier. Cela a empêché MySQL de changer l'ordre d'exécution et a fonctionné comme un charme!

5
Nicolas Thery

Si votre requête se termine par ORDER BY... LIMIT..., Il peut être optimal pour reformuler la requête afin d'inciter l'optimiseur à faire le LIMIT avant le JOIN.

(Cette réponse ne s'applique pas uniquement à la question d'origine sur STRAIGHT_JOIN, Ni à tous les cas de STRAIGHT_JOIN.)

En commençant par l'exemple de @Accountant م , cela devrait s'exécuter plus rapidement dans la plupart des situations. (Et cela évite d'avoir besoin d'indices.)

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;

Remarques:

  • Tout d'abord, 50 identifiants sont récupérés. Ce sera particulièrement rapide avec INDEX(date, id).
  • Ensuite, la jointure de retour à sales vous permet d'obtenir seulement 50 "whatevers" sans les transporter dans une table temporaire.
  • comme une sous-requête est, par définition, non ordonnée, le ORDER BY doit être répété dans la requête externe. (L'optimiseur peut trouver un moyen d'éviter de faire un autre tri.)
  • Oui, c'est plus compliqué. Mais c'est généralement plus rapide.

Je suis opposé à l'utilisation de hits parce que "Même si c'est plus rapide aujourd'hui, ça risque de ne pas l'être demain."

2
Rick James

Dans ma courte expérience, une des situations qui STRAIGHT_JOIN a réduit ma requête de 30 secondes à 100 millisecondes, c'est que la première table du plan d'exécution n'était pas la table contenant l'ordre par colonnes

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

SI l'optimiseur choisit de frapper stores en premier , cela causera Using index; Using temporary; Using filesort car

si ORDER BY ou GROUP BY contient des colonnes de tables autres que la première table de la file d'attente de jointure, une table temporaire est créée.

source

ici, l'optimiseur a besoin d'un peu d'aide en lui disant de frapper sales d'abord en utilisant

sales STRAIGHT_JOIN stores
1
Accountant م