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.