När MySQL-frågeoptimering nämns är index en av de första sakerna som tas upp. Idag ska vi försöka se varför de är så viktiga.
Vad är index?
I allmänhet är ett index en alfabetisk lista över poster med referenser till sidorna där de nämns. I MySQL är ett index en datastruktur som används för att snabbt hitta rader. Index kallas också för nycklar och dessa nycklar är avgörande för god prestanda - när data växer sig större kan behovet av att använda index på rätt sätt bli viktigare och viktigare. Att använda index är ett av de mest kraftfulla sätten att förbättra frågeprestanda - om index används på rätt sätt kan frågeprestanda öka med tiotals eller till och med hundratals gånger.
Idag kommer vi att försöka förklara de grundläggande fördelarna och nackdelarna med att använda index i MySQL. Tänk på att MySQL-index enbart förtjänar en hel bok så det här inlägget kommer inte att täcka absolut allt, men det kommer att vara en bra utgångspunkt. För den som är intresserad av hur index fungerar på ett djupare plan borde läsning av boken Relational Database Index Design and the Optimizers av Tapio Lahdenmäki och Michael Leach ge mer insikt.
Fördelarna med att använda index
Det finns några huvudsakliga fördelar med att använda index i MySQL och dessa är följande:
- Index gör det möjligt att snabbt hitta rader som matchar en WHERE-sats;
- Index kan hjälpa frågor att undvika att söka igenom vissa rader och därmed minska mängden data som servern behöver undersöka - om det finns ett val mellan flera index använder MySQL vanligtvis det mest selektiva indexet, det vill säga ett sådant index som hittar det minsta antalet rader;
- Index kan användas för att hämta rader från andra tabeller i JOIN-operationer;
- Index kan användas för att hitta det lägsta eller högsta värdet för en specifik kolumn som använder ett index;
- Index kan användas för att sortera eller gruppera en tabell om operationerna utförs på ett prefix längst till vänster i ett index - på samma sätt kan ett prefix längst till vänster för ett index med flera kolumner också användas av frågeoptimeraren att slå upp rader;
- Index kan också användas för att spara disk I/O - när ett täckande index används kan en fråga returnera värden direkt från indexstrukturen och spara disk I/O.
På liknande sätt finns det flera typer av index:
- INDEX är en typ av index där värden inte behöver vara unika. Den här typen av index accepterar NULL-värden;
- UNIQUE INDEX används ofta för att ta bort dubbletter av rader från en tabell - den här typen av index tillåter utvecklare att framtvinga unika radvärden;
- FULLTEXT INDEX är ett index som används på fält som använder fulltextsökningsfunktioner. Den här typen av index hittar nyckelord i texten istället för att direkt jämföra värden med värdena i indexet;
- DESCENDING INDEX är ett index som lagrar rader i fallande ordning - frågeoptimeraren kommer att välja den här typen av index när en fallande ordning begärs av frågan. Denna indextyp introducerades i MySQL 8.0;
- PRIMÄRNYCKEL är också ett index. I ett nötskal, PRIMÄRNYCKELN är en kolumn eller en uppsättning kolumner som identifierar varje rad i en tabell - används ofta tillsammans med fält som har ett AUTO_INCREMENT-attribut. Den här typen av index accepterar inte NULL-värden och när de väl har ställts in kan värdena i PRIMÄRKEYEN inte ändras.
Nu ska vi försöka gå igenom både fördelarna och nackdelarna med att använda index i MySQL. Vi börjar med den förmodligen mest diskuterade uppsidan - att påskynda frågor som matchar en WHERE-klausul.
Öppna sökningar som matchar en WHERE-klausul
Index används ofta för att påskynda sökfrågor som matchar en WHERE-sats. Anledningen till att ett index gör sådana sökoperationer snabbare är ganska enkel - frågor som använder ett index undviker en fullständig tabellsökning.
För att påskynda frågor som matchar en WHERE-sats kan du använda EXPLAIN-satsen i MySQL. Uttrycket EXPLAIN SELECT bör ge dig lite insikt om hur MySQL-frågeoptimeraren exekverar frågan - den kan också visa dig om frågan i fråga använder ett index eller inte och vilket index den använder. Ta en titt på följande frågeförklaring:
mysql> EXPLAIN SELECT * FROM demo_table WHERE field_1 = “Demo” \G;
*************************** 1. row ***************************
<...>
possible_keys: NULL
key: NULL
key_len: NULL
<...>
Frågan ovan använder inget index. Men om vi lägger till ett index på "field_1", kommer indexet att användas framgångsrikt:
mysql> EXPLAIN SELECT * FROM demo_table WHERE field_1 = “Demo” \G;
*************************** 1. row ***************************
<...>
possible_keys: field_1
key: field_1
key_len: 43
<...>
Möjliga_nycklar-kolumnen beskriver de möjliga index som MySQL kan välja, nyckelkolumnen beskriver det index som faktiskt valts och key_len-kolumnen beskriver längden på den valda nyckeln.
I det här fallet skulle MySQL utföra en sökning av värdena i indexet och returnera alla rader som innehåller det angivna värdet - som ett resultat skulle frågan bli snabbare. Även om index hjälper vissa frågor att bli snabbare, finns det ett par saker du måste tänka på om du vill att dina index ska hjälpa dina frågor:
- Isolera dina kolumner - MySQL kan inte använda index om kolumnerna som indexen används på inte är isolerade. Till exempel skulle en fråga som denna inte använda ett index:
SELECT field_1 FROM demo_table WHERE field_1 + 5 = 10;
För att lösa detta, lämna kolumnen som går efter WHERE-satsen ensam - förenkla din fråga så mycket som möjligt och isolera kolumnerna;
- Undvik att använda LIKE-frågor med ett föregående jokertecken - i det här fallet kommer MySQL inte att använda ett index eftersom det föregående jokertecken betyder att det kan finnas vad som helst före texten. Om du måste använda LIKE-frågor med jokertecken och vill att frågorna ska använda index, se till att jokertecken finns i slutet av söksatsen.
Naturligtvis kan snabba frågor som matchar en WHERE-sats också göras på andra sätt (till exempel partitionering), men för enkelhetens skull kommer vi inte att titta närmare på det i det här inlägget.
Det vi dock kan vara intresserade av är olika typer av indextyper, så vi ska titta närmare på det nu.
Att bli av med dubbletter av värden i en kolumn - UNIKA index
Syftet med ett UNIKT index i MySQL är att framtvinga det unika hos värdena i en kolumn. För att använda ett UNIKT index kör en CREATE UNIQUE INDEX-fråga:
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)
);
Det är allt som krävs för att lägga till ett unikt index i en tabell. Nu, när du försöker lägga till ett dubblettvärde till tabellen kommer MySQL tillbaka med följande fel:
#1062 - Duplicate entry ‘Demo’ for key ‘demo_column’
FULLTEXT-index
Ett FULLTEXT-index är ett sådant index som tillämpas på de kolumner som använder fulltextsökningsfunktioner. Den här typen av index har många unika funktioner inklusive stoppord och söklägen.
InnoDB-stoppordslistan har 36 ord medan MyISAM-stoppordslistan har 143. I InnoDB härleds stopporden från tabellen i variabeln innodb_ft_user_stopword_table, annars härleds de om denna variabel inte är inställd från variabeln innodb_ft_server_stopword_table. Om ingen av dessa två variabler är inställda använder InnoDB den inbyggda listan. För att se standardlistan för InnoDB-stoppord, fråga tabellen INNODB_FT_DEFAULT_STOPWORD.
I MyISAM härleds stopporden från filen storage/myisam/ft_static.c. Variabeln ft_stopword_file gör att standardstoppordslistan kan ändras. Stoppord kommer att inaktiveras om denna variabel är inställd på en tom sträng, men tänk på att om denna variabel definierar en fil, tolkas den definierade filen inte för kommentarer - MyISAM kommer att behandla alla ord som finns i filen som stoppord.
FULLTEXT-indexen är också kända för sina unika söklägen:
- Om en FULLTEXT-sökfråga utan modifierare körs, kommer ett naturligt språkläge att aktiveras. Det naturliga språkläget kan också aktiveras genom att använda modifieraren IN NATURAL LANGUAGE MODE;
- Modifieraren WITH QUERY EXPANSION aktiverar ett sökläge med sökexpansion. Ett sådant sökläge fungerar genom att utföra sökningen två gånger och när sökningen körs för andra gången skulle resultatuppsättningen innehålla några av de mest relevanta dokumenten från den första sökningen. I allmänhet är den här modifieraren användbar när användaren har viss kunskap (till exempel kan användaren söka efter "databas" och hoppas att se "InnoDB" och "MyISAM" i resultatuppsättningen);
- Modifieraren I BOOLENSK LÄGE tillåter sökning med booleska operatorer. Till exempel skulle operatorerna +, - eller * utföra olika uppgifter - operatorn + skulle definiera att värdet måste finnas i en rad, operatorn - skulle definiera att värdet inte får existera och operatorn * skulle fungera som en jokertecken.
En fråga som använder ett FULLTEXT-index ser ut så här:
SELECT * FROM demo_table WHERE MATCH(demo_field) AGAINST(‘value’ IN NATURAL LANGUAGE MODE);
Tänk på att FULLTEXT-index i allmänhet är användbara för MATCH() AGAINST()-operationer - inte för WHERE-operationer, vilket innebär att om en WHERE-sats skulle användas, användbarheten av att använda olika indextyper skulle inte elimineras.
Det är också värt att nämna att FULLTEXT-index har en minsta teckenlängd. I InnoDB kan en FULLTEXT-sökning endast utföras när sökfrågan består av minst tre tecken - denna gräns ökas till fyra tecken i MyISAM-lagringsmotorn.
FALLANDE index
Ett sjunkande index är ett sådant index där InnoDB lagrar posterna i en fallande ordning - frågeoptimeraren kommer att använda ett sådant index när en fallande ordning begärs av frågan. Ett sådant index kan läggas till i en kolumn genom att köra en fråga som nedan:
CREATE INDEX descending_index ON demo_table(column_name DESC);
Ett stigande index kan också läggas till i en kolumn - ersätt bara DESC med ASC.
PRIMÄRA NYCKLAR
En PRIMÄRNYCKEL fungerar som en unik identifierare för varje rad i en tabell. En kolumn med en PRIMARY KEY måste innehålla unika värden - inga NULL-värden får heller användas. Om ett duplikatvärde läggs till i en kolumn som har en PRIMÄRNYCKEL, kommer MySQL att svara med ett fel #1062:
#1062 - Duplicate entry ‘Demo’ for key ‘PRIMARY’
Om ett NULL-värde läggs till i kolumnen kommer MySQL att svara med ett fel #1048:
#1048 - Column ‘id’ cannot be null
Primära index kallas ibland även klustrade index (vi diskuterar dem senare).
Du kan också skapa index på flera kolumner samtidigt - sådana index kallas multikolumnindex.
Index för flera kolumner
Index på flera kolumner missförstås ofta - ibland indexerar utvecklare och DBA:er alla kolumner separat eller indexerar dem i fel ordning. För att göra frågor som använder index med flera kolumner så effektiva som möjligt, kom ihåg att ordningen på kolumner i index som använder mer än en kolumn är en av de vanligaste orsakerna till förvirring i det här utrymmet - eftersom det inte finns några "denna vägen eller vägen" ” indexordningslösningar, måste du komma ihåg att den korrekta ordningen på flerkolumnindex beror på de frågor som använder indexet. Även om detta kan verka ganska uppenbart, kom ihåg att kolumnordningen är viktig när du hanterar index med flera kolumner - välj kolumnordningen så att den är så selektiv som möjligt för de frågor som körs oftast.
För att mäta selektiviteten för specifika kolumner, få förhållandet mellan antalet distinkta indexerade värden och det totala antalet rader i tabellen - kolumnen som har den högre selektiviteten bör vara den första .
Ibland behöver du också indexera mycket långa teckenkolumner, och i så fall kan du ofta spara tid och resurser genom att indexera de första tecknen - ett prefix - istället för hela värdet.
Prefixindex
Prefixindex kan vara användbara när kolumnerna innehåller mycket långa strängvärden, vilket skulle innebära att ett index på hela kolumnen skulle förbruka mycket diskutrymme. MySQL hjälper till att lösa detta problem genom att tillåta dig att bara indexera ett prefix av värdet som i sin tur gör indexstorleken mindre. Ta en titt:
CREATE TABLE `demo_table` (
`demo_column` VARCHAR(100) NOT NULL,
INDEX(demo_column(10))
);
Frågan ovan skulle skapa ett prefixindex i demokolumnen som endast indexerar de första 10 tecknen i värdet. Du kan också lägga till ett prefixindex till en befintlig tabell:
CREATE INDEX index_name ON table_name(column_name(length));
Så, om du till exempel vill indexera de första 5 tecknen i en demo_column på en demo_table, kan du köra följande fråga:
CREATE INDEX demo_index ON demo_table(demo_column(5));
Du bör välja ett prefix som är tillräckligt långt för att ge selektivitet, men också tillräckligt kort för att ge utrymme. Detta kan dock vara lättare sagt än gjort - du måste experimentera och hitta den lösning som fungerar för dig.
Täckande index
Ett täckande index "täcker" alla fält som krävs för att utföra en fråga. Med andra ord, när alla fält i en fråga täcks av ett index, används ett täckande index. Till exempel för en fråga som så:
SELECT id, title FROM demo_table WHERE id = 1;
Ett täckande index kan se ut så här:
INDEX index_name(id, title);
Om du vill försäkra dig om att en fråga använder ett täckande index, utfärda en EXPLAIN-sats på den och ta en titt på Extra-kolumnen. Till exempel, om din tabell har ett flerkolumnindex på id och titel och en fråga som endast kommer åt dessa två kolumner exekveras, kommer MySQL att använda indexet:
mysql> EXPLAIN SELECT id, title FROM demo_table \G;
*************************** 1. row ***************************
<...>
type: index
key: index_name
key_len: 5
rows: 1000
Extra: Using index
<...>
Tänk på att ett täckande index måste lagra värdena från de kolumner som det täcker. Det betyder att MySQL endast kan använda B-Tree-index för att täcka frågor eftersom andra typer av index inte lagrar dessa värden.
Klustrade, sekundära index och indexkardinalitet
När index diskuteras kan du också höra termerna klustrade, sekundära index och indexkardinalitet. Enkelt uttryckt är klustrade index ett tillvägagångssätt för datalagring och alla andra index än klustrade index är sekundära index. Indexkardinalitet å andra sidan är antalet unika värden i ett index.
Ett klustrat index påskyndar frågor eftersom nära värden också lagras nära varandra på disken, men det är också anledningen till att du bara kan ha ett klustrat index i en tabell.
Ett sekundärt index är vilket index som helst som inte är det primära indexet. Ett sådant index kan ha dubbletter.
Nackdelarna med att använda index
Användningen av index har förvisso fördelar, men vi får inte glömma att index kan vara en av de främsta orsakerna till problem i MySQL också. Några av nackdelarna med att använda index är följande:
- Index kan försämra prestandan för vissa frågor - även om index tenderar att påskynda prestandan för SELECT-frågor, saktar de ner prestandan för INSERT-, UPDATE- och DELETE-frågor eftersom när data uppdateras index måste också uppdateras tillsammans med det:alla åtgärder som involverar manipulering av indexen kommer att vara långsammare än vanligt;
- Index förbrukar diskutrymme - ett index upptar sitt eget utrymme, så indexerad data kommer också att förbruka mer diskutrymme;
- Redundanta och dubblerade index kan vara ett problem - MySQL låter dig skapa dubbletter av index på en kolumn och det "skyddar dig" inte från att göra ett sådant misstag. Ta en titt på det här exemplet:
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) );
En oerfaren användare kanske tror att den här frågan får id-kolumnen att öka automatiskt och sedan lägger till ett index på kolumnen och gör att kolumnen inte accepterar dubbletter av värden. Detta är dock inte vad som händer här. I det här fallet har samma kolumn tre index på sig:ett vanligt INDEX, och eftersom MySQL implementerar både PRIMÄRKEY och UNIKA begränsningar med index, lägger det till ytterligare två index på samma kolumn!
Slutsats
Sammanfattningsvis har index i MySQL sin egen plats - index kan användas i en mängd olika scenarier, men vart och ett av dessa användningsscenarier har sina egna nackdelar som måste beaktas för att få ut det mesta av index som används.
För att använda index väl, profilera dina frågor, ta en titt på vilka alternativ du har när det kommer till index, känna till deras fördelar och nackdelar, bestäm vilka index du behöver baserat på dina krav och efter att du har indexerat kolumnerna, se till att dina index är faktiskt används av MySQL. Om du har indexerat ditt schema ordentligt bör prestandan för dina frågor förbättras, men om svarstiden inte tillfredsställer dig, se om ett bättre index kan skapas för att förbättra det.