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

Ovlivňuje pořadí polí v klauzuli WHERE výkon v MySQL?

SQL byl navržen jako deklarativní jazyk, ne jako procedurální. Optimalizátor dotazů by tedy neměl zvažte pořadí predikátů klauzule where při určování, jak je použít.

Pravděpodobně budu waaaay příliš zjednodušovat následující diskusi o optimalizátoru dotazů SQL. Napsal jsem před lety v tomto smyslu (byla to spousta legrace!). Pokud se opravdu chcete ponořit do moderní optimalizace dotazů, podívejte se na ladění SQL , od O'Reilly.

V jednoduchém optimalizátoru dotazů SQL se příkaz SQL nejprve zkompiluje do stromu relační algebry operace. Každá z těchto operací bere jednu nebo více tabulek jako vstup a vytváří další tabulku jako výstup. Skenovat je sekvenční skenování, které načítá tabulku z databáze. Řadit vytvoří setříděnou tabulku. Vyberte vytvoří tabulku, jejíž řádky jsou vybrány z jiné tabulky podle nějaké podmínky výběru. Projekt vytvoří tabulku pouze s určitými sloupci jiné tabulky. Více produktů vezme dvě tabulky a vytvoří výstupní tabulku složenou z každého myslitelného párování jejich řádků.

Klauzule SQL SELECT je matoucí zkompilována do relační algebry Projekt , zatímco klauzule WHERE se změní na relační algebru Vybrat . Klauzule FROM se změní na jedno nebo více Připojení , každý vezme dva stoly a vytvoří jeden stůl ven. Existují další operace relační algebry zahrnující sjednocení množin, průnik, rozdíl a členství, ale pojďme to jednoduše.

Tento strom opravdu potřebuje optimalizovat. Pokud například máte:

select E.name, D.name 
from Employee E, Department D 
where E.id = 123456 and E.dept_id = D.dept_id

s 5 000 zaměstnanci v 500 odděleních spuštění neoptimalizovaného stromu slepě vytvoří všechny možné kombinace jednoho zaměstnance a jednoho oddělení (Křížový produkt ) a poté Vybrat z jediné kombinace, která byla potřeba. Skenovat of Employee vytvoří tabulku s 5 000 záznamy, Skenovat oddělení vytvoří tabulku s 500 záznamy, Křížový produkt z těchto dvou tabulek vytvoří tabulku 2 500 000 záznamů a Vybrat na E.id vezme tuto tabulku 2 500 000 záznamů a zahodí všechny kromě jednoho, záznamu, který byl požadován.

[Skutečný dotazovací procesor se samozřejmě pokusí nezhmotnit všechny tyto přechodné tabulky v paměti.]

Optimalizátor dotazů tedy prochází stromem a aplikuje různé optimalizace. Jedním z nich je rozdělit každý Vybrat do řetězce Výběrů , jeden pro každý z původních Vybrat podmínky nejvyšší úrovně, ty a-ed dohromady. (Tomu se říká "konjunktivní normální forma".) Potom jednotlivé menší Selects se pohybují ve stromu a slučují se s jinými operacemi relační algebry, aby vytvořily efektivnější operace.

Ve výše uvedeném příkladu optimalizátor nejprve stiskne Vybrat na E.id =123456 níže pod drahým křížovým produktem úkon. To znamená Křížový produkt pouze vytvoří 500 řádků (jeden pro každou kombinaci daného zaměstnance a jednoho oddělení). Poté nejvyšší úroveň Vybrat for E.dept_id =D.dept_id odfiltruje 499 nežádoucích řádků. Není to špatné.

Pokud je v poli ID zaměstnance index, může optimalizátor kombinovat Skenování zaměstnance pomocí Vybrat na E.id =123456, aby se vytvořil rychlý index Vyhledat . To znamená, že se do paměti z disku načte pouze jeden řádek zaměstnance namísto 5 000. Věci se hledají nahoru.

Poslední hlavní optimalizací je vzít Vybrat na E.dept_id =D.dept_id a zkombinujte jej s Křížovým produktem . Tím se změní na relační algebru Equijoin úkon. Tohle samo o sobě moc nedělá. Ale pokud je na Department.dept_id index, pak sekvenční Skenovat nižší úrovně oddělení krmení Equijoin lze změnit na velmi rychlý index Vyhledání záznamu oddělení našeho jednoho zaměstnance.

Menší optimalizace zahrnují prosazení Projektu operace dolů. Pokud nejvyšší úroveň vašeho dotazu potřebuje pouze E.name a D.name a podmínky vyžadují E.id, E.dept_id a D.dept_id, pak Skenovat operace nemusí vytvářet přechodné tabulky se všemi ostatními sloupci, což šetří místo během provádění dotazu. Strašně pomalý dotaz jsme změnili na dvě vyhledávání v indexu a nic moc jiného.

Abychom se dostali více k původní otázce, řekněme, že máte:

select E.name 
from Employee E 
where E.age > 21 and E.state = 'Delaware'

Neoptimalizovaný strom relační algebry, když je spuštěn, naskenuje 5 000 zaměstnanců a vytvoří, řekněme, 126 v Delaware, kteří jsou starší než 21 let. Optimalizátor dotazů má také určitou hrubou představu o hodnotách v databázi. Mohl by vědět, že sloupec E.state obsahuje 14 států, ve kterých má společnost pobočky, a něco o distribucích E.age. Nejprve tedy zjistí, zda je některé pole indexováno. Pokud je E.state, má smysl používat tento index pouze k výběru malého počtu zaměstnanců, o kterých má procesor dotazů podezření, že jsou v Delaware na základě jeho posledních vypočítaných statistik. Pokud je pouze E.age, zpracovatel dotazu pravděpodobně rozhodne, že to za to nestojí, protože 96 % všech zaměstnanců je 22 let a více. Pokud je tedy E.state indexován, náš dotazovací procesor přeruší Select a sloučí E.state ='Delaware' s Skenovat proměnit jej v mnohem efektivnější Index Scan .

Řekněme v tomto příkladu, že na E.state a E.age nejsou žádné indexy. Kombinované Vybrat operace proběhne po sekvenčním "Skenování" zaměstnance. Je rozdíl, která podmínka v Vybrat je hotovo jako první? Pravděpodobně nic moc. Procesor dotazů je může ponechat v původním pořadí v příkazu SQL, nebo může být o něco sofistikovanější a podívat se na očekávané náklady. Ze statistik by opět zjistil, že podmínka E.state ='Delaware' by měla být více selektivní, takže by obrátila podmínky a udělala by to jako první, takže existuje pouze 126 E.age> 21 srovnání namísto 5 000 . Nebo si může uvědomit, že porovnávání rovnosti řetězců je mnohem dražší než porovnávání celých čísel, a ponechat pořadí na pokoji.

V každém případě je to vše velmi složité a je velmi nepravděpodobné, že by vaše pořadí syntaktických podmínek něco změnilo. Nedělal bych si s tím starosti, pokud nemáte skutečný problém s výkonem a váš prodejce databáze nepoužije pořadí podmínek jako nápovědu.



  1. Udělte oprávnění pro konkrétní databázi v PostgreSQL

  2. Příklady UTC_DATE – MySQL

  3. Těšíme se na PGConf India 2017

  4. Provádění více SQL dotazů v jednom příkazu pomocí PHP