J'avais juste 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'indication FORCE INDEX. Je suis tombé sur le mot clé de jointure STRAIGHT_JOIN et j'ai commencé à remplacer certains de mes mots clés INNER JOIN par lui. 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 elle s'exécute maintenant 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?
straight_join
.À partir de la 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."
la source
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 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 équivalents.
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 jusqu'à 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 rejoint avantb
.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 par une petite table et joindre de telle sorte que la jointure résultante soit également petite est idéal. Les choses prennent la forme d'une poire si commencer par une petite table et la joindre à une plus grande table finit par être aussi grande que la grande table.
Cela dépend cependant 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 où tous
straight_join
les indices d'index obligatoires ou agressifs sont des requêtes qui paginent sur un grand nombre de données dans un ordre de tri strict avec un filtrage de la lumière. MySQL préfère fortement utiliser des index pour tous les filtres et jointures sur les tris; cela a du sens car la plupart des gens n'essaient pas de trier toute la base de données 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 de filtrer la table entière, qu'elle soit triée ou ne pas. Dans ce cas, mettre une jointure directe immédiatement après la table qui avait la colonne indexée que je voulais trier sur des choses fixes.la source
straight_join
évalue la table de gauche avant la droite. Donc, si vous voulez partir deA -> B -> C
mon exemple, le premierjoin
mot-clé pourrait être remplacé parstraight_join
.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 straight_join, la requête exécute les jointures dans l'ordre dans lequel elles sont spécifiées. En plaçant la table comme le plus petit dénominateur commun en premier et en spécifiant straight_join, vous pouvez améliorer les performances de la requête.
la source
STRAIGHT_JOIN
, en utilisant cette clause, vous pouvez contrôler l'JOIN
ordre: quelle table est analysée dans la boucle externe et laquelle est dans la boucle interne.la source
Je vais vous expliquer pourquoi j'ai dû utiliser STRAIGHT_JOIN:
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!
la source
Dans ma courte expérience, l'une des situations qui
STRAIGHT_JOIN
a réduit ma requête de 30 secondes à 100 millisecondes est que la première table du plan d'exécution n'était pas la table qui a 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 en
stores
premier, cela provoqueraUsing index; Using temporary; Using filesort
carla source
ici l'optimiseur a besoin d'un peu d'aide en lui disant de frapper d'
sales
abord en utilisantla source
Si votre requête se termine par
ORDER BY... LIMIT...
, il peut être optimal de reformuler la requête pour inciter l'optimiseur à faire leLIMIT
avant leJOIN
.(Cette réponse ne s'applique pas uniquement à la question initiale sur
STRAIGHT_JOIN
, ni à tous les cas deSTRAIGHT_JOIN
.)En commençant par l' exemple de @Accountant م , cela devrait fonctionner 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:
INDEX(date, id)
.sales
vous permet d'obtenir seulement 50 "whatevers" sans les transporter dans une table temporaire.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.)Je m'oppose à l'utilisation des hits parce que «même si c'est plus rapide aujourd'hui, ça risque de ne pas être plus rapide demain».
la source
Je sais que c'est un peu vieux mais voici un scénario, j'ai fait un script batch pour peupler une certaine table. À un moment donné, la requête s'est déroulée très lentement. Il semble que l'ordre de jointure était incorrect sur des enregistrements particuliers:
Une commande incorrecte s'exécute pendant environ 65 secondes lors de l'utilisation de straight_join s'exécute en millisecondes
la source
--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
la source