Tady je to, co se děje.
The SELECT COUNT (...) icd_index where icd='25000'
použije index, což je strom BTree oddělený od dat. Ale skenuje to tímto způsobem:
- Najděte první položku s icd='25000'. To je téměř okamžité.
- Skenujte dopředu, dokud nenajde změnu v icd. Tím se naskenuje pouze index, data se nedotknou. Podle EXPLAIN bude k naskenování asi 910 104 položek rejstříku.
Nyní se podívejme na BTree pro tento index. Na základě polí v indexu bude mít každý řádek přesně 22 bajtů, plus bude nějaká režie (odhad 40 %). Indexový blok MyISAM je 1 kB (srovnej 16 kB InnoDB). Odhadoval bych 33 řádků na blok. 910,104/33 říká, že k provedení COUNT je potřeba přečíst asi 27 kB bloků. (Poznámka:COUNT(core_id)
potřebuje zkontrolovat core_id
protože je null, COUNT(*)
ne; to je drobný rozdíl.) Čtení 27K bloků na obyčejném pevném disku trvá asi 270 sekund. Měli jste štěstí, že jste to zvládli za 60 sekund.
Druhý běh našel všechny tyto bloky v key_buffer (za předpokladu, že key_buffer_size je alespoň 27 MB), takže nemusel čekat na disk. Proto to bylo mnohem rychlejší. (Toto ignoruje mezipaměť dotazů, kterou jste měli rozumně vyprázdnit nebo použít SQL_NO_CACHE.)
5.6 je náhodou irelevantní (ale díky za zmínku), protože tento proces se od verze 4.0 nebo dříve nezměnil (kromě toho, že utf8 neexistovalo; více o tom níže).
Přechod na InnoDB by pomohl několika způsoby. PRIMÁRNÍ KLÍČ by byl „seskupován“ s daty, nikoli uložen jako samostatný strom BTree. Jakmile jsou tedy data nebo PK uložena do mezipaměti, ostatní jsou okamžitě k dispozici. Počet bloků by byl spíše 5K, ale byly by to bloky 16KB. Tyto mohou být načteny rychleji, pokud je mezipaměť studená.
Ptáte se „Potřebuji index pouze na icd?“ – No, to by zmenšilo velikost MyISAM BTree na asi 21 bajtů na řádek, takže BTree by bylo asi 21/27 velikosti, což není příliš zlepšení (alespoň pro situace studené mezipaměti).
Další myšlenka je, pokud icd
je vždy číselné a vždy číselné, chcete-li použít MEDIUMINT UNSIGNED
a připněte na ZEROFILL
pokud může mít úvodní nuly.
Jejda, nevšiml jsem si CHARACTER SET. (Výše uvedená čísla jsem opravil, ale dovolte mi to upřesnit.)
- CHAR(5) umožňuje 5 znaků .
- ascii zabírá 1 bajt za znak .
- utf8 zabírá až 3 bajty za znaků .
- Takže CHAR(5) CHARACTER SET utf8 zabere 15 bajtů vždy .
Změna sloupce na CHAR(5) CHARACTER SET ascii
by se zmenšil na 5 bajtů.
Změna na MEDIUMINT UNSIGNED ZEROFILL by jej zmenšila na 3 bajty.
Zmenšení dat by zrychlilo I/O o zhruba úměrnou hodnotu (po povolení dalších 6 bajtů pro další dvě pole.