Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment puis-je éviter une analyse complète de la table sur cette requête mysql ?

Basé sur EXPLAIN sortie dans votre question, vous avez déjà tous les index que la requête devrait utiliser, à savoir :

CREATE INDEX idx_zip_from_distance
  ON zipcode_distances (zipcode_from, distance, zipcode_to);
CREATE INDEX idx_zipcode ON venues (zipcode, id);
CREATE INDEX idx_venue_id ON events (venue_id);

(Je ne suis pas sûr d'après vos noms d'index si idx_zip_from_distance inclut vraiment le zipcode_to colonne. Si ce n'est pas le cas, vous devez l'ajouter pour en faire un index de couverture . De plus, j'ai inclus le venues.id colonne dans idx_zipcode par souci d'exhaustivité, mais, en supposant qu'il s'agisse de la clé primaire de la table et que vous utilisez InnoDB, elle sera de toute façon automatiquement incluse.)

Cependant, il semble que MySQL choisisse un plan de requête différent, et peut-être sous-optimal, où il analyse tous les événements, trouve leurs lieux et codes postaux, et filtre ensuite les résultats sur la distance. Cela pourrait être le plan de requête optimal, si la cardinalité de la table des événements était suffisamment faible, mais du fait que vous posez cette question, je suppose que ce n'est pas le cas.

Une des raisons du plan de requête sous-optimal pourrait être le fait que vous en avez trop des index qui déroutent le planificateur. Par exemple, êtes-vous vraiment besoin de ces trois index sur la table des codes postaux, étant donné que les données qu'il stocke sont vraisemblablement symétriques ? Personnellement, je suggérerais uniquement l'index que j'ai décrit ci-dessus, plus un index unique (qui peut également être la clé primaire, si vous n'en avez pas une artificielle) sur (zipcode_to, zipcode_from) (de préférence dans cet ordre, afin que toute requête occasionnelle sur zipcode_to=? peut en faire usage).

Cependant, sur la base de certains tests que j'ai effectués, je soupçonne que le principal problème pour lequel MySQL choisit le mauvais plan de requête se résume simplement aux cardinalités relatives de vos tables. Vraisemblablement, vos zipcode_distances réels la table est énorme , et MySQL n'est pas assez intelligent pour réaliser à quel point les conditions dans WHERE clause vraiment réduire.

Si tel est le cas, la solution la meilleure et la plus simple consiste peut-être simplement à forcer MySQL pour utiliser les index que vous voulez :

select
    *
from
    zipcode_distances z 
    FORCE INDEX (idx_zip_from_distance)
inner join
    venues v    
    FORCE INDEX (idx_zipcode)
    on z.zipcode_to=v.zipcode
inner join
    events e
    FORCE INDEX (idx_venue_id)
    on v.id=e.venue_id
where
    z.zipcode_from='92108' and
    z.distance <= 5

Avec cette requête, vous devriez en effet obtenir le plan de requête souhaité. (Vous avez besoin de FORCE INDEX ici, puisqu'avec juste USE INDEX le planificateur de requêtes pourrait toujours décider d'utiliser une analyse de table au lieu de l'index suggéré, ce qui irait à l'encontre de l'objectif. Cela m'est arrivé lorsque j'ai testé cela pour la première fois.)

Ps. Voici une démo sur SQLize, à la fois avec et sans FORCE INDEX , démontrant le problème.