Com base no
EXPLAIN
output em sua pergunta, você já tem todos os índices que a consulta deveria estar usando, a saber: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);
(Não tenho certeza de seus nomes de índice se
idx_zip_from_distance
realmente inclui o zipcode_to
coluna. Caso contrário, você deve adicioná-lo para torná-lo um índice de cobertura
. Além disso, incluí o venues.id
coluna em idx_zipcode
para completar, mas, supondo que seja a chave primária da tabela e que você esteja usando o InnoDB, ela será incluída automaticamente de qualquer maneira.) No entanto, parece que o MySQL está escolhendo um plano de consulta diferente e possivelmente abaixo do ideal, onde ele verifica todos os eventos, encontra seus locais e códigos postais e só então filtra os resultados por distância. Isso poderia ser o plano de consulta ideal, se a cardinalidade da tabela de eventos for baixa o suficiente, mas pelo fato de você estar fazendo essa pergunta, suponho que não seja.
Um motivo para o plano de consulta abaixo do ideal poderia seja o fato de você ter muitos índices que confundem o planejador. Por exemplo, você realmente precisa de todos esses três índices na tabela de CEP, já que os dados armazenados são presumivelmente simétricos? Pessoalmente, sugiro apenas o índice que descrevi acima, além de um índice exclusivo (que também pode ser a chave primária, se você não tiver uma artificial) em
(zipcode_to, zipcode_from)
(de preferência nessa ordem, para que quaisquer consultas ocasionais em zipcode_to=?
pode fazer uso). No entanto, com base em alguns testes que fiz, suspeito que o principal problema pelo qual o MySQL está escolhendo o plano de consulta errado se resume simplesmente às cardinalidades relativas de suas tabelas. Presumivelmente, suas
zipcode_distances
reais a mesa é enorme , e o MySQL não é inteligente o suficiente para perceber o quanto as condições no WHERE
cláusula realmente reduzi-lo. Nesse caso, a melhor e mais simples correção pode ser simplesmente forçar MySQL para usar os índices que você deseja :
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
Com essa consulta, você deve obter o plano de consulta desejado. (Você precisa de
FORCE INDEX
aqui, pois com apenas USE INDEX
o planejador de consulta ainda pode decidir usar uma varredura de tabela em vez do índice sugerido, anulando o propósito. Isso aconteceu quando testei isso pela primeira vez.) Ps. Aqui está uma demonstração do SQLize, tanto com e sem
FORCE INDEX
, demonstrando o problema.