Když se zmiňuje optimalizace dotazů MySQL, indexy jsou jednou z prvních věcí, které se zabývají. Dnes se pokusíme zjistit, proč jsou tak důležité.
Co jsou indexy?
Obecně je rejstřík abecední seznam záznamů s odkazy na stránky, na kterých jsou zmíněny. V MySQL je index datová struktura používaná k rychlému nalezení řádků. Indexy se také nazývají klíče a tyto klíče jsou kritické pro dobrý výkon – jak se data zvětšují, potřeba správného používání indexů může být stále důležitější. Používání indexů je jedním z nejúčinnějších způsobů, jak zlepšit výkon dotazů – pokud jsou indexy používány správně, výkon dotazů se může zvýšit desetinásobně nebo dokonce stokrát.
Dnes se pokusíme vysvětlit základní výhody a nevýhody používání indexů v MySQL. Mějte na paměti, že samotné indexy MySQL si zaslouží celou knihu, takže tento příspěvek nepokryje úplně všechno, ale bude dobrým výchozím bodem. Pro ty, kteří se zajímají o to, jak indexy fungují na hlubší úrovni, by si měli přečíst knihu Relational Database Index Design and the Optimizers od Tapio Lahdenmäkiho a Michaela Leacha.
Výhody používání indexů
Používání indexů v MySQL má několik hlavních výhod:
- Indexy umožňují rychle najít řádky odpovídající klauzuli WHERE;
- Indexy mohou dotazům pomoci vyhnout se prohledávání určitých řádků, čímž se sníží množství dat, která server potřebuje prozkoumat – pokud existuje výběr mezi více indexy, MySQL obvykle používá nejselektivnější index, tj. index, který najde nejmenší počet řádků;
- Indexy lze použít k načtení řádků z jiných tabulek v operacích JOIN;
- Indexy lze použít k nalezení minimální nebo maximální hodnoty konkrétního sloupce, který používá index;
- Indexy lze použít k řazení nebo seskupení tabulky, pokud se operace provádějí na předponě indexu zcela vlevo – podobně může optimalizátor dotazů použít také předponu zcela vlevo u indexu s více sloupci vyhledávat řádky;
- Indexy lze také použít k uložení I/O disku – když se používá krycí index, dotaz může vrátit hodnoty přímo ze struktury indexu, která šetří I/O disku.
Podobně existuje několik typů indexů:
- INDEX je typ indexu, kde hodnoty nemusí být jedinečné. Tento typ indexu přijímá hodnoty NULL;
- UNIQUE INDEX se často používá k odstranění duplicitních řádků z tabulky – tento typ indexu umožňuje vývojářům vynutit jedinečnost hodnot řádků;
- INDEX FULLTEXTU je index, který se používá na pole využívající možnosti fulltextového vyhledávání. Tento typ indexu vyhledá klíčová slova v textu namísto přímého porovnávání hodnot s hodnotami v indexu;
- DESCENDING INDEX je index, který ukládá řádky v sestupném pořadí – optimalizátor dotazů zvolí tento typ indexu, když dotaz požaduje sestupné pořadí. Tento typ indexu byl zaveden v MySQL 8.0;
- PRIMARY KEY je také index. Stručně řečeno, PRIMÁRNÍ KLÍČ je sloupec nebo sada sloupců, které identifikují každý řádek v tabulce – často se používá společně s poli s atributem AUTO_INCREMENT. Tento typ indexu nepřijímá hodnoty NULL a jakmile je jednou nastaven, hodnoty v PRIMARY KEY nelze změnit.
Nyní se pokusíme projít výhody i nevýhody používání indexů v MySQL. Začneme pravděpodobně nejčastěji diskutovanou výhodou – urychlením dotazů, které odpovídají klauzuli WHERE.
Urychlení dotazů odpovídajících klauzuli WHERE
Indexy se často používají k urychlení vyhledávacích dotazů, které odpovídají klauzuli WHERE. Důvod, proč index urychluje takové vyhledávací operace, je docela jednoduchý – dotazy, které používají index, se vyhýbají úplnému prohledávání tabulky.
Abyste urychlili dotazy, které odpovídají klauzuli WHERE, můžete v MySQL použít příkaz EXPLAIN. Příkaz EXPLAIN SELECT by vám měl poskytnout určitý přehled o tom, jak optimalizátor dotazů MySQL provádí dotaz – může vám také ukázat, zda daný dotaz používá index nebo ne a jaký index používá. Podívejte se na následující vysvětlení dotazu:
mysql> EXPLAIN SELECT * FROM demo_table WHERE field_1 = “Demo” \G;
*************************** 1. row ***************************
<...>
possible_keys: NULL
key: NULL
key_len: NULL
<...>
Výše uvedený dotaz nepoužívá index. Pokud však přidáme index na „pole_1“, bude index úspěšně použit:
mysql> EXPLAIN SELECT * FROM demo_table WHERE field_1 = “Demo” \G;
*************************** 1. row ***************************
<...>
possible_keys: field_1
key: field_1
key_len: 43
<...>
Sloupec possible_keys popisuje možné indexy, které může MySQL zvolit, sloupec key popisuje skutečně zvolený index a sloupec key_len popisuje délku zvoleného klíče.
V tomto případě by MySQL provedlo vyhledání hodnot v indexu a vrátilo by všechny řádky obsahující zadanou hodnotu – ve výsledku by byl dotaz rychlejší. I když indexy pomáhají určitým dotazům být rychlejší, je třeba mít na paměti několik věcí, pokud chcete, aby vám indexy pomáhaly s dotazy:
- Izolujte své sloupce – MySQL nemůže používat indexy, pokud sloupce, na kterých se indexy používají, nejsou izolované. Například dotaz jako tento by nepoužil index:
SELECT field_1 FROM demo_table WHERE field_1 + 5 = 10;
Chcete-li to vyřešit, ponechte sloupec, který následuje za klauzulí WHERE, na pokoji – co nejvíce zjednodušte svůj dotaz a izolujte sloupce;
- Vyhněte se používání dotazů LIKE s předchozím zástupným znakem – v tomto případě MySQL nepoužije index, protože předchozí zástupný znak znamená, že před textem může být cokoli. Pokud musíte používat dotazy LIKE se zástupnými znaky a chcete, aby dotazy využívaly indexy, ujistěte se, že zástupný znak je na konci vyhledávacího příkazu.
Urychlení dotazů, které odpovídají klauzuli WHERE, lze samozřejmě provést i jinými způsoby (například rozdělením), ale v zájmu jednoduchosti se tím v tomto příspěvku nebudeme dále zabývat.
Co by nás však mohlo zajímat, jsou různé druhy typů indexů, takže se na to nyní podíváme.
Odstranění duplicitních hodnot ve sloupci – UNIKÁTNÍ indexy
Účelem UNIQUE indexu v MySQL je vynutit jedinečnost hodnot ve sloupci. Chcete-li použít UNIQUE index, spusťte dotaz CREATE UNIQUE INDEX:
CREATE UNIQUE INDEX demo_index ON demo_table(demo_column);
You can also create a unique index when you create a table:
CREATE TABLE demo_table (
`demo_column` VARCHAR(100) NOT NULL,
UNIQUE KEY(demo_column)
);
To je vše, co je potřeba k přidání jedinečného indexu do tabulky. Nyní, když se pokusíte přidat duplicitní hodnotu do tabulky, MySQL se vrátí s následující chybou:
#1062 - Duplicate entry ‘Demo’ for key ‘demo_column’
FULLTEXTOVÉ indexy
FULLTEXTOVÝ index je takový index, který se aplikuje na sloupce, které využívají možnosti fulltextového vyhledávání. Tento typ indexu má mnoho jedinečných schopností včetně ignorovaných slov a režimů vyhledávání.
Seznam stopwordů InnoDB má 36 slov, zatímco seznam stopwordů MyISAM má 143. V InnoDB jsou ignorovaná slova odvozena z tabulky nastavené v proměnné innodb_ft_user_stopword_table, jinak, pokud tato proměnná není nastavena, jsou odvozena z proměnné innodb_ft_server_stopword_table. Pokud není nastavena žádná z těchto dvou proměnných, InnoDB použije vestavěný seznam. Chcete-li zobrazit výchozí seznam ignorovaných slov InnoDB, dotazujte se v tabulce INNODB_FT_DEFAULT_STOPWORD.
V MyISAM jsou ignorovaná slova odvozena ze souboru storage/myisam/ft_static.c. Proměnná ft_stopword_file umožňuje změnit výchozí seznam ignorovaných slov. Stopwords budou zakázány, pokud je tato proměnná nastavena na prázdný řetězec, ale mějte na paměti, že pokud tato proměnná definuje soubor, definovaný soubor není analyzován pro komentáře - MyISAM bude považovat všechna slova nalezená v souboru za ignorovaná slova.
Fulltextové indexy jsou také známé svými jedinečnými režimy vyhledávání:
- Pokud je spuštěn vyhledávací dotaz FULLTEXT bez modifikátorů, aktivuje se režim přirozeného jazyka. Režim přirozeného jazyka lze také aktivovat pomocí modifikátoru V REŽIMU PŘIROZENÉHO JAZYKA;
- Modifikátor WITH QUERY EXPANSION umožňuje režim vyhledávání s rozšířením dotazu. Takový režim vyhledávání funguje tak, že se vyhledávání provádí dvakrát, a když je vyhledávání spuštěno podruhé, sada výsledků bude obsahovat několik nejrelevantnějších dokumentů z prvního vyhledávání. Obecně je tento modifikátor užitečný, když má uživatel nějaké předpokládané znalosti (uživatel může například hledat „databáze“ a doufat, že ve výsledné sadě uvidí „InnoDB“ a „MyISAM“).
- Modifikátor IN BOOLEAN MODE umožňuje vyhledávání pomocí booleovských operátorů. Například operátory +, - nebo * by každý vykonával různé úkoly - operátor + by definoval, že hodnota musí být přítomna v řádku, operátor - by definoval, že hodnota nesmí existovat a operátor * by fungoval jako zástupný znak.
Dotaz, který používá FULLTEXTOVÝ index, vypadá takto:
SELECT * FROM demo_table WHERE MATCH(demo_field) AGAINST(‘value’ IN NATURAL LANGUAGE MODE);
Mějte na paměti, že FULLTEXTOVÉ indexy jsou obecně užitečné pro operace MATCH() AGAINST() – nikoli pro operace WHERE, což znamená, že pokud by byla použita klauzule WHERE, užitečnost použití různých typů indexů by nebyla vyloučena.
Za zmínku také stojí, že FULLTEXTOVÉ indexy mají minimální délku znaků. V InnoDB lze FULLTEXTOVÉ vyhledávání provádět pouze v případě, že vyhledávací dotaz obsahuje minimálně tři znaky – tento limit je v úložišti MyISAM zvýšen na čtyři znaky.
DESCENDING Indexs
DESCENDING index je takový index, kde InnoDB ukládá položky v sestupném pořadí – optimalizátor dotazů takový index použije, když dotaz požaduje sestupné pořadí. Takový index lze přidat do sloupce spuštěním dotazu jako níže:
CREATE INDEX descending_index ON demo_table(column_name DESC);
Do sloupce lze přidat také vzestupný index – stačí nahradit DESC za ASC.
PRIMÁRNÍ KLÍČE
PRIMÁRNÍ KLÍČ slouží jako jedinečný identifikátor pro každý řádek v tabulce. Sloupec s PRIMÁRNÍM KLÍČEM musí obsahovat jedinečné hodnoty – nelze použít ani hodnoty NULL. Pokud je do sloupce, který má PRIMÁRNÍ KLÍČ přidána duplicitní hodnota, MySQL odpoví chybou #1062:
#1062 - Duplicate entry ‘Demo’ for key ‘PRIMARY’
Pokud je do sloupce přidána hodnota NULL, MySQL odpoví chybou #1048:
#1048 - Column ‘id’ cannot be null
Primární indexy se také někdy nazývají seskupené indexy (probereme je později).
Můžete také vytvářet indexy pro více sloupců najednou – takové indexy se nazývají vícesloupcové indexy.
Vícesloupcové indexy
Indexy ve více sloupcích jsou často nepochopeny – někdy vývojáři a správci databáze indexují všechny sloupce samostatně nebo je indexují ve špatném pořadí. Aby byly dotazy využívající vícesloupcové indexy co nejúčinnější, pamatujte, že pořadí sloupců v indexech, které používají více než jeden sloupec, je jednou z nejčastějších příčin zmatku v tomto prostoru – protože neexistují žádné „tudy ani dálnice“. ” řešení pořadí indexů, musíte si uvědomit, že správné pořadí vícesloupcových indexů závisí na dotazech, které index používají. I když se to může zdát docela zřejmé, pamatujte, že pořadí sloupců je při práci s vícesloupcovými indexy zásadní – zvolte pořadí sloupců tak, aby bylo co nejselektivnější pro dotazy, které se budou spouštět nejčastěji.
Aby bylo možné měřit selektivitu pro konkrétní sloupce, získejte poměr počtu různých indexovaných hodnot k celkovému počtu řádků v tabulce – sloupec, který má vyšší selektivitu, by měl být první .
Někdy také potřebujete indexovat velmi dlouhé sloupce znaků a v takovém případě můžete často ušetřit čas a prostředky indexováním prvních několika znaků – předpony – namísto celé hodnoty.
Indexy předpon
Indexy předpon mohou být užitečné, pokud sloupce obsahují hodnoty velmi dlouhého řetězce, což by znamenalo, že přidání indexu na celý sloupec by spotřebovalo hodně místa na disku. MySQL pomáhá tento problém řešit tím, že vám umožňuje indexovat pouze předponu hodnoty, což zase snižuje velikost indexu. Podívejte se:
CREATE TABLE `demo_table` (
`demo_column` VARCHAR(100) NOT NULL,
INDEX(demo_column(10))
);
Výše uvedený dotaz by ve sloupci ukázky vytvořil index předpony, který by indexoval pouze prvních 10 znaků hodnoty. Můžete také přidat index předpony do existující tabulky:
CREATE INDEX index_name ON table_name(column_name(length));
Pokud byste například chtěli indexovat prvních 5 znaků demo_column v demo_table, můžete spustit následující dotaz:
CREATE INDEX demo_index ON demo_table(demo_column(5));
Měli byste zvolit předponu, která je dostatečně dlouhá, aby poskytovala selektivitu, ale také dostatečně krátká, aby poskytovala prostor. Možná se to snadněji řekne, než udělá – musíte experimentovat a najít řešení, které vám vyhovuje.
Pokrývající indexy
Pokrývající index „pokrývá“ všechna požadovaná pole pro provedení dotazu. Jinými slovy, když jsou všechna pole v dotazu pokryta indexem, používá se krycí index. Například pro dotaz typu:
SELECT id, title FROM demo_table WHERE id = 1;
Pokrytí indexu může vypadat takto:
INDEX index_name(id, title);
Pokud se chcete ujistit, že dotaz používá krycí index, zadejte pro něj příkaz EXPLAIN a pak se podívejte do sloupce Extra. Pokud má například vaše tabulka vícesloupcový index na id a title a je proveden dotaz, který přistupuje pouze k těmto dvěma sloupcům, MySQL použije index:
mysql> EXPLAIN SELECT id, title FROM demo_table \G;
*************************** 1. row ***************************
<...>
type: index
key: index_name
key_len: 5
rows: 1000
Extra: Using index
<...>
Mějte na paměti, že krycí index musí ukládat hodnoty ze sloupců, které pokrývá. To znamená, že MySQL může používat pouze indexy B-Stromu k pokrytí dotazů, protože jiné druhy indexů tyto hodnoty neukládají.
Skupinové, sekundární indexy a kardinalita indexu
Když se mluví o indexech, můžete také zaslechnout termíny seskupený, sekundární indexy a kardinalita indexu. Zjednodušeně řečeno, klastrované indexy jsou přístupem k ukládání dat a všechny indexy jiné než klastrované jsou sekundární indexy. Na druhé straně kardinalita indexu je počet jedinečných hodnot v indexu.
Clusterovaný index urychluje dotazy, protože blízké hodnoty jsou na disku také uloženy blízko sebe, ale to je také důvod, proč můžete mít v tabulce pouze jeden seskupený index.
Sekundární index je jakýkoli index, který není primárním indexem. Takový index může mít duplikáty.
Nevýhody používání indexů
Použití indexů má určitě své výhody, ale nesmíme zapomínat, že indexy mohou být také jednou z hlavních příčin problémů v MySQL. Některé z nevýhod používání indexů jsou následující:
- Indexy mohou snížit výkon určitých dotazů – i když indexy obvykle zrychlují výkon dotazů SELECT, zpomalují výkon dotazů INSERT, UPDATE a DELETE, protože když jsou data aktualizována, index je také třeba aktualizovat společně s ním:jakákoli operace, která zahrnuje manipulaci s indexy, bude pomalejší než obvykle;
- Indexy zabírají místo na disku – index zabírá svůj vlastní prostor, takže indexovaná data zaberou také více místa na disku;
- Redundantní a duplicitní indexy mohou být problémem – MySQL vám umožňuje vytvářet duplicitní indexy na sloupci a „neochrání vás“ před takovou chybou. Podívejte se na tento příklad:
CREATE TABLE `demo_table` ( `id` INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY, `column_2` VARCHAR(10) NOT NULL, `column_3` VARCHAR(10) NOT NULL, INDEX(id), UNIQUE(id) );
Nezkušený uživatel si může myslet, že tento dotaz způsobí, že se sloupec id automaticky zvýší, pak přidá index do sloupce a sloupec nebude přijímat duplicitní hodnoty. To však není to, co se zde děje. V tomto případě má stejný sloupec tři indexy:běžný INDEX, a protože MySQL implementuje omezení PRIMARY KEY i UNIQUE s indexy, přidá dva další indexy do stejného sloupce!
Závěr
Abych to uzavřel, indexy v MySQL mají své vlastní místo – indexy lze použít v mnoha scénářích, ale každý z těchto scénářů použití má své vlastní stinné stránky, které je třeba vzít v úvahu, abyste z nich získali maximum indexy, které se používají.
Chcete-li indexy dobře používat, profilujte své dotazy, podívejte se, jaké máte možnosti, pokud jde o indexy, poznejte jejich výhody a nevýhody, na základě svých požadavků se rozhodněte, jaké indexy potřebujete, a po indexování sloupců se ujistěte, že jsou vaše indexy ve skutečnosti používá MySQL. Pokud jste své schéma indexovali správně, výkon vašich dotazů by se měl zlepšit, ale pokud vás doba odezvy neuspokojuje, zjistěte, zda nelze vytvořit lepší index, abyste jej vylepšili.