Na základě EXPLAIN
výstup ve vaší otázce, již máte všechny indexy, které by dotaz měl používat, jmenovitě:
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);
(Z názvů vašich indexů si nejsem jistý, zda idx_zip_from_distance
skutečně obsahuje zipcode_to
sloupec. Pokud ne, měli byste jej přidat, aby se z něj stal zahrnující index
. Také jsem zahrnul venues.id
ve sloupci idx_zipcode
pro úplnost, ale za předpokladu, že se jedná o primární klíč pro tabulku a že používáte InnoDB, bude stejně zahrnut automaticky.)
Zdá se však, že MySQL volí jiný a možná neoptimální plán dotazů, kde prohledá všechny události, najde jejich místa konání a PSČ a teprve poté filtruje výsledky podle vzdálenosti. To mohlo být optimální plán dotazů, pokud by mohutnost tabulky událostí byla dostatečně nízká, ale ze skutečnosti, že se ptáte na tuto otázku, předpokládám, že není.
Jeden důvod pro neoptimální plán dotazů mohl být tím, že jich máte příliš mnoho indexy, které jsou pro plánovače matoucí. Například opravdu potřebujete všechny tři tyto indexy v tabulce PSČ, vzhledem k tomu, že data, která ukládá, jsou pravděpodobně symetrická? Osobně bych navrhoval pouze index, který jsem popsal výše, plus jedinečný index (který může být také primárním klíčem, pokud nemáte umělý klíč) na (zipcode_to, zipcode_from)
(nejlépe v tomto pořadí, aby se případné občasné dotazy na zipcode_to=?
může to využít).
Na základě některých testů, které jsem provedl, se však domnívám, že hlavní problém, proč MySQL vybírá špatný plán dotazů, spočívá jednoduše v relativní mohutnosti vašich tabulek. Pravděpodobně vaše skutečné zipcode_distances
stůl je obrovský a MySQL není dostatečně chytré, aby si uvědomilo, jak moc jsou podmínky v WHERE
klauzule to opravdu zúží.
Pokud ano, nejlepší a nejjednodušší opravou může být jednoduše vynutit MySQL používat indexy, které chcete :
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
S tímto dotazem byste měli skutečně získat požadovaný plán dotazů. (Potřebujete FORCE INDEX
zde, protože pouze pomocí USE INDEX
plánovač dotazů by se přesto mohl rozhodnout použít místo navrhovaného indexu prohledávání tabulky, čímž byl zmařen účel. Stalo se mi to, když jsem to poprvé testoval.)
Ps. Zde je ukázka na SQLize, obě s
a bez
FORCE INDEX
, což demonstruje problém.