Den første ting du skal vide er, at indekser er en måde at undgå at scanne hele tabellen for at opnå det resultat, du leder efter.
Der er forskellige slags indekser, og de er implementeret i lagringslaget, så der er ingen standard mellem dem, og de afhænger også af den lagringsmotor, du bruger.
InnoDB og B+Tree-indekset
For InnoDB er den mest almindelige indekstype det B+Tree-baserede indeks, der gemmer elementerne i en sorteret rækkefølge. Du behøver heller ikke få adgang til den rigtige tabel for at få de indekserede værdier, hvilket gør, at din forespørgsel vender tilbage meget hurtigere.
"Problemet" ved denne indekstype er, at du skal forespørge efter værdien længst til venstre for at bruge indekset. Så hvis dit indeks har to kolonner, sig efternavn og fornavn, er den rækkefølge, du forespørger på disse felter, betydende meget .
Så givet følgende tabel:
CREATE TABLE person (
last_name VARCHAR(50) NOT NULL,
first_name VARCHAR(50) NOT NULL,
INDEX (last_name, first_name)
);
Denne forespørgsel ville drage fordel af indekset:
SELECT last_name, first_name FROM person
WHERE last_name = "John" AND first_name LIKE "J%"
Men den følgende ville ikke
SELECT last_name, first_name FROM person WHERE first_name = "Constantine"
Fordi du forespørger på first_name
kolonne først, og det er ikke kolonnen længst til venstre i indekset.
Dette sidste eksempel er endnu værre:
SELECT last_name, first_name FROM person WHERE first_name LIKE "%Constantine"
For nu sammenligner du den del af feltet længst til højre i indekset.
Hash-indekset
Dette er en anden indekstype, som desværre kun hukommelsesbackend understøtter. Det er lynhurtigt, men kun nyttigt til fulde opslag, hvilket betyder, at du ikke kan bruge det til operationer som >
, <
eller LIKE
.
Da det kun virker til hukommelsesbackend, vil du sandsynligvis ikke bruge det ret ofte. Den vigtigste sag, jeg kan komme i tanke om lige nu, er den, at du opretter en midlertidig tabel i hukommelsen med et sæt resultater fra en anden udvælgelse og udfører en masse andre valg i denne midlertidige tabel ved hjælp af hash-indekser.
Hvis du har en stor VARCHAR
felt, kan du "emulere" brugen af et hash-indeks, når du bruger et B-Tree, ved at oprette en anden kolonne og gemme en hash af den store værdi på den. Lad os sige, at du gemmer en url i et felt, og værdierne er ret store. Du kan også oprette et heltalsfelt kaldet url_hash
og brug en hash-funktion som CRC32
eller en hvilken som helst anden hash-funktion til at hash url'en, når du indsætter den. Og så, når du skal forespørge efter denne værdi, kan du gøre noget som dette:
SELECT url FROM url_table WHERE url_hash=CRC32("http://gnu.org");
Problemet med ovenstående eksempel er, at siden CRC32
funktionen genererer en ganske lille hash, vil du ende med en masse kollisioner i de hash-værdier. Hvis du har brug for nøjagtige værdier, kan du løse dette problem ved at gøre følgende:
SELECT url FROM url_table
WHERE url_hash=CRC32("http://gnu.org") AND url="http://gnu.org";
Det er stadig værd at hashe ting, selvom kollisionstallet er højt, fordi du kun udfører den anden sammenligning (strengen) med de gentagne hashes.
Desværre, ved at bruge denne teknik, er du stadig nødt til at ramme tabellen for at sammenligne url
felt.
Afslut
Nogle fakta, som du kan overveje, hver gang du vil tale om optimering:
-
Heltalssammenligning er langt hurtigere end strengsammenligning. Det kan illustreres med eksemplet om emulering af hash-indekset i
InnoDB
. -
Måske gør tilføjelse af yderligere trin i en proces den hurtigere, ikke langsommere. Det kan illustreres ved, at du kan optimere en
SELECT
ved at opdele den i to trin, så den første lagrer værdier i en nyoprettet tabel i hukommelsen, og kør derefter de tungere forespørgsler på denne anden tabel.
MySQL har også andre indekser, men jeg tror, at B+Tree er det mest brugte nogensinde, og hash-en er en god ting at vide, men du kan finde de andre i MySQL-dokumentation .
Jeg anbefaler dig stærkt at læse "High Performance MySQL"-bogen, svaret ovenfor var bestemt baseret på kapitlet om indekser.