web-dev-qa-db-fra.com

MySQL: Index lors de l'adhésion à des tables non utilisées (question d'optimisation des performances)

J'ai besoin d'optimiser la requête suivante:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM blogposts
JOIN articles ON articles.blogpost_id = blogposts.id
WHERE blogposts.deleted = 0
AND blogposts.title LIKE '%{de}%'
AND blogposts.visible = 1
AND blogposts.date_published <= NOW()
ORDER BY blogposts.date_created DESC
LIMIT 0 , 50

Expliquez Select me donne le résultat suivant:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE articles ALL blogpost_id NULL NULL NULL 6915 Using temporary; Using filesort
1 SIMPLE blogposts eq_ref PRIMARY PRIMARY 4 articles.blogpost_id 1 Using where

Pourquoi faut-il d'abord les articles, puis les blogposts? Est-ce parce que les blogposts ont plus d'entrées? Et comment puis-je améliorer la requête afin que l'articlepost puisse utiliser un index?

Mise à jour : Un index est défini sur blogpostS.date_Created. Supprimer le blogpostS.title comme condition comme et la date_publied <= () ne fait rien.

Quand je supprimai le "articles.ID comme articleId" Il peut utiliser l'index blogpost_id sur les articles ... sonne étrangement pour moi, quelqu'un sait pourquoi? (parce que j'en ai besoin ..)

Le nouvelle expliquer ressemble à ceci:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  articles    index blogpost_id blogpost_id    4    NULL    6915    Using index; Using temporary; Using filesort
1   SIMPLE  blogposts   eq_ref  PRIMARY PRIMARY 4   articles.blogpost_id    1   Using where
6
Michael Weibel

J'ai examiné de plus près la requête et vous pourrez peut-être la repenser. Voici ce que je veux dire:

La limite de 0,50 partie de la requête semble être occupée dans la requête en dernier.

Vous pouvez améliorer la disposition de la requête en procédant comme suit:

Étape 1) Créez une requête en ligne pour ne recueillir que des clés. Dans ce cas, l'ID pour les blogposts.

Étape 2) Imposez-y où, commander et groupe par clauses sur la requête en ligne apportant des clés.

Étape 3) Impostissez la clause limite comme dernière étape consistant à faire la requête en ligne.

Étape 4) Joignez-vous à la requête en ligne avec la table des blogposost dans l'événement que vous avez besoin de colonnes supplémentaires de Blogpost comme plongevest

Étape 5) Rejoignez ce nouveau blogpossique tentable avec la table d'articles.

Les étapes 1 à 3 sont destinées à créer une tentable avec exactement 50 rangées et incluent l'identifiant Blogpost. Ensuite, effectuez toutes les jointures mortes.

Avec ces étapes appliquées à votre requête d'origine, vous devriez avoir ceci:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
  SELECT B.*
  FROM
  (
    SELECT id FROM blogposts
    WHERE date_published <= NOW()
    AND deleted = 0 AND visible = 1
    AND title LIKE '%{de}%'
    ORDER BY date_created DESC
    LIMIT 0,50
  ) A
  INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;

Depuis que vous avez édité la question et indiquez que vous allez supprimer comme si votre requête devrait maintenant ressembler davantage à ceci:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
  SELECT B.*
  FROM
  (
    SELECT id FROM blogposts
    WHERE date_published <= NOW()
    AND deleted = 0 AND visible = 1
    ORDER BY date_created DESC
    LIMIT 0,50
  ) A
  INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;

Dans la phrase [choses omises], si vous n'avez besoin de rien des blogposts autres que les clés, votre requête doit ressembler à ceci:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
  SELECT id FROM blogposts
  WHERE date_published <= NOW()
  AND deleted = 0 AND visible = 1
  ORDER BY date_created DESC
  LIMIT 0,50
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;

CAVEAT

Assurez-vous de créer un index impliquant les colonnes supprimées, visibles et Date_Créré comme suit:

ALTER TABLE blogposts ADD INDEX deleted_visible_date_created (deleted,visible,date_created);

Essaie !!!

4
RolandoMySQLDBA

Votre endroit où condition blogposts.title LIKE '%{de}%' provoquera une numérisation de table complète sur la table blogposts Tableau. Il est probable que les figures MySQL numérisant 6915 articles sont plus efficaces.

Quant à la manière de l'améliorer, vous pouvez ajouter un index sur blogposts à l'aide de date_created ou alors date_published et ajoutez une plage de la condition où l'état (autre que <= maintenant ()))

4
Derek Downey

Et blogpostos.title comme '% {de}%' - inutile pour l'optimisation (carte sauvage principale)

Où blogpostosts.deletted = 0 et blogpost.Visible = 1 - Yuck: S'ils ne devraient pas être affichés, sortez-les de la table.

Et blogposts.date_publihed <= () Commande par blogpost.date_Created Desc Limite 0, 50 - Cela conduit à besoin d'index (Date_Publied) (Cependant, les drapeaux et les drapeaux peuvent empêcher cet index d'être utilisé.)

S'il vous plaît fournir une table de création de spectacle ...; Afficher le statut de table ...;

3
Rick James

Merci beaucoup :)

Je l'ai donné un essai et j'ai découvert que votre dernier commentaire sur l'index était la chose dont j'avais besoin. Parfois, ce n'est que quelques petites améliorations nécessaires ...;) Comparaison:

Ancienne requête :

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM blogposts
JOIN articles ON articles.blogpost_id = blogposts.id
WHERE blogposts.deleted = 0
AND blogposts.title LIKE '%{de}%'
AND blogposts.visible = 1
AND blogposts.date_published <= NOW()
ORDER BY blogposts.date_created DESC
LIMIT 0 , 50

nouvelle requête :

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
    SELECT B.*
    FROM
    (
        SELECT id FROM blogposts
        WHERE date_published <= NOW()
        AND deleted = 0 AND visible = 1
        AND title LIKE '%{de}%'
        ORDER BY date_created DESC
        LIMIT 0,50
    ) A
    INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id

Maintenant sans l'indice mentionné:

Ancien expliquant le résultat :

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE articles ALL blogpost_id NULL NULL NULL 6915 Using temporary; Using filesort
1 SIMPLE blogposts eq_ref PRIMARY PRIMARY 4 articles.blogpost_id 1 Using where

Nouveau résultat d'explication :

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    50   
1   PRIMARY     articles    ref     blogposts_id    blogposts_id    4   blogposts.id    1    
2   DERIVED     <derived3>  ALL     NULL    NULL    NULL    NULL    50   
2   DERIVED     B   eq_ref  PRIMARY     PRIMARY     4   A.id    1    
3   DERIVED     blogposts   ALL     deleted,visible,date_published  deleted     1       28198   Using filesort

expliquer le résultat avec l'index sur la vieille requête :

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  blogposts   ref     PRIMARY,deleted,visible,date_published,deleted_vis...   deleted_visible_date_created    2   const,const     27771   Using where
1   SIMPLE  articles    ref     blogposts_id    blogposts_id    4   db.blogposts.id     1

Expliquez le résultat avec l'index sur la nouvelle requête :

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    50   
1   PRIMARY     articles    ref     blogposts_id    blogposts_id    4   blogposts.id    1    
2   DERIVED     <derived3>  ALL     NULL    NULL    NULL    NULL    50   
2   DERIVED     B   eq_ref  PRIMARY     PRIMARY     4   A.id    1    
3   DERIVED     blogposts   ref     deleted,visible,date_published,deleted_visible_dat...   deleted_visible_date_created    2       27771   Using where

vitesse sur la vieille requête sans/avec index : 0.1835/0.0037

Vitesse de la nouvelle requête sans/avec index : 0.1883/0.0035

Ainsi, à cause de la différence marginale juste entre l'ancienne requête/la nouvelle requête, je préfère toujours utiliser l'ancienne requête, mais avec l'index. Mais je garderai cela à l'esprit, comme si un jour, la vieille requête est trop lente :)

Que serait intéressant pour moi, c'est de savoir comment vous avez eu l'idée de définir l'index comme celui-ci? Je pense que j'ai publié cette question, j'ai également essayé avec deleted_visible, mais au lieu de date_créé, j'ai utilisé date_publihed (comme dans l'endroit où la clause) ...

Merci :)

Mise à jour par Rolandomysqldba 2011-05-19 13:

Ce qui me l'a donné était l'endroit où et l'ordre par des clauses.

Dans la clause où, supprimé (0) et visible (1) sont des valeurs statiques. Dans la clause de l'ordre, la date_created était comme une cible en mouvement parmi toutes les lignes avec supprimées = 0 et visible = 1. Donc, j'ai placé les variables statiques devant l'indice d'abord, puis la cible en mouvement durer dans l'index. Habituellement, cela fait partie du principe sous-jacent de refactoriser les déclarations SQL. Vous avez besoin d'index qui appuiera votre lieu, groupe par et commander des clauses.

2
Michael Weibel