När vi arbetar med Ruby on Rails-ramverket hanterar vi vanligtvis relationsdatabaser som MySQL eller PostgreSQL. När vi definierar migreringar med hjälp av Active Record Migrations stöter vi på de så kallade indexen, men nybörjare förstår ofta inte riktigt index och vilka fördelar de ger.
När vi arbetar med Ruby on Rails-ramverket hanterar vi vanligtvis relationsdatabaser som MySQL eller PostgreSQL. När vi definierar migreringar med hjälp av Active Record Migrations stöter vi på de så kallade indexen, men nybörjare förstår ofta inte riktigt index och vilka fördelar de ger.
I det här inlägget vill jag förklara vad index är, vad de används till och presentera några goda exempel på hur man kan använda dem.
Databas
Det finns många databasmotorer, och en av de mest populära är de tidigare nämnda MySQL, PostgreSQL, Oracle eller Microsoft SQL Server. De är alla relationsdatabaser, vilket innebär att alla data är relaterade till varandra och lagras i tabeller. Varje tabellrad kallas en post och var och en har sin egen unika identifierare (id). Du kan kontrollera rankningen av de mest populära databasmotorerna på https://db-engines.com/en/ranking. Där hittar du också några icke-relationella databaser, t.ex. MongoDB.
Skapa ett index
Tabellerna i våra databaser kan ha från bara några få till flera dussin - i extrema fall upp till flera hundra - kolumner. Tänk på att varje tabell kan ha ett obegränsat antal rader. Detta antal är inte ett direkt resultat av databasens struktur och vi bör alltid anta att antalet poster kommer att öka successivt och att vår databas därmed kommer att växa. Inledande antaganden och frågor som skrivs i befintliga applikationer kan vara bra för ett litet eller medelstort antal poster, men med tiden, när mer data anländer, upphör applikationens kommunikation med databasen att vara effektiv.
Programmerarens roll är att skriva frågor för att hämta vissa data från tabellen eller tabellerna, men det optimala sättet att behandla frågan beror på databasmotorn. Kom ihåg att databasmotorerna laddar data från disken till minnet och sedan skannar dem. Detta innebär att om många användare utför komplexa operationer samtidigt, kommer flera av dem att få vänta på sin tur på grund av bristen på resurser för att genomföra sina sökningar. Det är därför som relevanta index är så viktiga.
Wiki: Index - en datastruktur som ökar hastigheten för att utföra sökoperationer på en tabell.
För varje index måste vi definiera nycklar (för en eller flera kolumner) som ska användas för att söka efter poster i tabellen. Data i indexet kommer att sorteras med den nyckel som tidigare har definierats, vilket avsevärt kommer att påskynda sökningen efter data i tabellen. Det enklaste exemplet från vardagslivet är en telefonkatalog där personer sorteras efter för- och efternamn. Man kan säga att vårt index i det här fallet kommer att vara för- och efternamnet.
Hur väljer du den bästa indexnyckeln? Det är inte svårt - kom bara ihåg några regler. Skapa ett index baserat på kolumner som:
- kommer ofta att användas i våra förfrågningar (WHERE),
- i kombination med varandra ger ett unikt värde (dvs. ett värde som kommer att ange exakt en rad),
- kommer att användas som så kallade anslutningskolumner (JOIN),
- ger de mest selektiva nycklarna, dvs. de som ger det minsta antalet rader när man skriver en fråga.
Om vi redan vet vilka nycklar som är optimala för vår tabell kan vi också fråga oss hur många index vi behöver. I det här fallet är det bäst att känna till de frågor som kommer att hänvisa till vår tabell redan i designstadiet.
Låt oss skapa index för specifika frågor som kommer att visas, men skriv dem inte för varje kolumn. Index, liksom tabeller, måste lagras någonstans, så när vi skapar tabeller med ett index för varje kolumn måste vi ta hänsyn till att mängden utrymme som används kan öka avsevärt.
Skapa ett unikt index
En annan fråga som vi måste tänka på är unikhet. Det är värt att ägna fem minuter extra åt att fundera över om vårt index verkligen är unikt. På så sätt berättar vi för frågeoptimeraren att den inte behöver förvänta sig dubbletter på frågan. Till exempel e-postadresser:
frozenstringliteral: true
klass CreateUsers < ActiveRecord::Migration[6.0]
def ändra
createtable :users do |t|
t.string :email, null: false
end
addindex :users, :email, unik: true
end
slut
På exemplet med PostgreSQL-motorn kommer jag att visa skillnaden i frågehastighet på e-postkolumnen med ett unikt index och utan ett index.
1. Du kan använda prov kod på din egen databas för att kunna testa exemplet nedan. Låt oss först skapa en tom tabell med en kolumn:
CREATE TABLE users (
e-post varchar
);
2. Låt oss generera 10 000 poster för testet:
DO $
BEGIN FOR i IN 1..10000 LOOP
INSERT INTO users values((select 'user' || i || '@example.com'));
SLUTA LOOP; SLUTA;
$;
Vi kommer att använda EXPLAIN ANALYZE för att kontrollera hur snabbt vår fråga kommer att behandlas när vi vill hitta en specifik användare i databasen.
EXPLAIN ANALYZE SELECT email FROM users WHERE email = 'user890example.com';
Vår fråga tvingar fram en iteration runt hela bordet för att hitta den post som intresserar oss.
Denna process kallas sekventiell skanning. I det här fallet är det bästa sättet att läsa hela tabellen och filtrera bort vissa rader.
PostgreSQL kommer att filtrera bort de onödiga raderna och helt enkelt returnera de som intresserar oss. Det här är verkligen det bästa att göra i det här fallet. Sekventiell skanning är inte alltid dålig, det finns fall där sekventiell skanning är idealisk.
4. Nu är det dags att kontrollera den fråga som redan gjorts på den tabell som har INDEX UNIQUE. Låt oss ställa in indexet och köra frågan.
EATE UNIQUE INDEX index_email på users(email);
EXPLAIN ANALYZE SELECT email FROM users WHERE email = 'user890example.com';
Den här gången utnyttjade PostgreSQL indexskanning eftersom alla kolumner som behövs redan finns i indexet.
Om du bara väljer några få rader blir det mycket effektivt att använda indexet. Men om fler data ska väljas blir det alltför tidskrävande att skanna indexet och tabellen.
Sammanfattning
Som du kan se är körtiden för en fråga i en kolumn med ett index mycket kortare (i exemplet som visas är det en minskning från 1,267 ms till 0,111 ms, så mycket som 91,24%!) Den viktigaste skillnaden är hur PostgreSQL söker efter den post som intresserar oss. I det första fallet var databasmotorn tvungen att söka i hela tabellen efter den post vi behövde. I det andra är dock indexstrukturen sorterad och unik, varför motorn visste var posten befann sig, vilket avsevärt påskyndade tiden för frågebehandlingen.
När det gäller stora databaser och mycket komplexa frågor kan korrekt inställda index avsevärt påskynda arbetet med din applikation utan att du behöver öka hastigheten på den maskin som du söker i databasen på.
Det är värt att komma ihåg att det inte är en bra metod att skapa index på varje kolumn. Etablerade index kommer att påskynda optimeringsarbetet när du söker efter intressanta data, men samtidigt sakta ner införandet av nya och uppdatering av befintliga.