sql >> Databáze >  >> RDS >> Mysql

Jak se mohu vyhnout úplnému skenování tabulky u tohoto dotazu mysql?

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.



  1. Jak velká může být databáze MySQL, než začne klesat výkon

  2. Generujte DDL programově na Postgresql

  3. Získejte směr indexu z informačního schématu v MySQL

  4. Jak používat zápas proti v mysql