Ruby on Rails raamistikuga töötades tegeleme tavaliselt relatsiooniliste andmebaasidega nagu MySQL või PostgreSQL. Migratsioonide määratlemisel Active Record Migrations abil puutume kokku nn indeksitega, kuid algajad ei mõista tihtipeale päris täpselt indekseid ja nende eeliseid.
Ruby on Rails raamistikuga töötades tegeleme tavaliselt relatsiooniliste andmebaasidega nagu MySQL või PostgreSQL. Migratsioonide määratlemisel Active Record Migrations abil puutume kokku nn indeksitega, kuid algajad ei mõista tihtipeale päris täpselt indekseid ja nende eeliseid.
Selles postituses tahaksin selgitada, mis on indeksid, milleks neid kasutatakse ja esitan mõned head tavad, kuidas neid kasutada.
Andmebaas
Andmebaasimootoreid on palju ja üks populaarsemaid on eelnevalt mainitud MySQL, PostgreSQL, Oracle või Microsoft SQL Server. Need kõik on relatsioonilised andmebaasid, mis tähendab, et kõik andmed on omavahel seotud ja salvestatud tabelites. Iga tabelirea nimetatakse kirjeks ja igaühel on oma unikaalne identifikaator (id). Kõige populaarsemate andmebaasimootorite edetabelit saate vaadata aadressil https://db-engines.com/en/ranking. Sealt leiate ka mõned mitterelatsioonilised andmebaasid, näiteks MongoDB.
Indeksi loomine
Meie andmebaaside tabelites võib olla vaid mõned kuni mitukümmend - äärmuslikel juhtudel kuni mitusada - veergu. Pidage meeles, et igal tabelil võib olla piiramatu arv ridu. See arv ei tulene otseselt andmebaasi struktuurist ja me peaksime alati eeldama, et kirjete arv suureneb järjest ja sellest tulenevalt kasvab ka meie andmebaas. Esialgsed eeldused ja olemasolevates rakendustes kirjutatud päringud võivad olla suurepärased väikese või keskmise arvu kirjete puhul, kuid aja jooksul, kui andmeid saabub rohkem, lakkab rakenduse suhtlus andmebaasiga olemast tõhus.
Programmeerija ülesanne on kirjutada päringuid, et saada tabelist või tabelitest mõningaid andmeid, kuid päringu töötlemise optimaalne viis sõltub andmebaasimootorist. Pidage meeles, et andmebaasimootorid laadivad andmed kettalt mällu ja seejärel skaneerivad neid. See tähendab, et kui mitu kasutajat sooritavad korraga keerulisi operatsioone, peavad mitmed neist ootama oma järjekorda, sest otsingute teostamiseks puuduvad ressursid. Seepärast on asjakohased indeksid nii olulised.
Wiki: Indeks - andmestruktuur, mis suurendab tabelis otsinguoperatsioonide tegemise kiirust.
Iga indeksi jaoks peame määratlema võtmed (ühe või mitme veeru jaoks), mida kasutatakse kirjete otsimiseks tabelis. Andmed indeksis sorteeritakse eelnevalt määratletud võtme järgi, mis kiirendab oluliselt andmete otsimist tabelis. Kõige lihtsam näide igapäevaelust on telefoniraamat, kus inimesed on sorteeritud nime ja perekonnanime järgi. Võib öelda, et meie indeksiks on sel juhul ees- ja perekonnanimi.
Kuidas valida parim indeksivõti? See ei ole keeruline - lihtsalt pidage meeles mõned reeglid. Loo indeks, mis põhineb veergudel, mis:
- kasutatakse sageli meie päringutes (WHERE),
- annavad koos üksteisega unikaalse väärtuse (st sellise, mis näitab täpselt ühte rida),
- kasutatakse nn ühendavate veergudena (JOIN),
- annab kõige selektiivsemad võtmed, st need, mis annavad päringu kirjutamisel kõige väiksema arvu ridu tagasi.
Kui me juba teame, millised võtmed on meie tabeli jaoks optimaalsed, võime ka küsida, kui palju indekseid me vajame. Sellisel juhul on kõige parem teada päringuid, mis hakkavad meie tabelile viitama, juba projekteerimise etapis.
Loome indeksid konkreetsete päringute jaoks, mis ilmuvad, kuid ei kirjuta neid iga veeru jaoks. Indekseid, nagu ka tabeleid, tuleb kuskil hoida, seega kui loome tabelid, kus iga veeru jaoks on indeks, peame arvestama, et kasutatava ruumi hulk võib oluliselt suureneda.
Unikaalse indeksi loomine
Teine küsimus, millele me peame mõtlema, on ainulaadsus. Tasub kulutada viis minutit, et mõelda, kas meie indeks on tõesti ainulaadne. Nii ütleme päringu optimeerijale, et ta ei pea päringu puhul ootama duplikaate. Näiteks e-posti aadressid:
frozenstringliteral: true
class CreateUsers < ActiveRecord::Migration[6.0]
def change
createtable :users do |t|
t.string :email, null: false
end
addindex :users, :email, unique: true
end
end
PostgreSQL-i mootori näitel näitan ma unikaalse indeksiga ja ilma indeksita e-posti veeru päringukiiruse erinevust.
1. Võite kasutada proovi kood snippets oma andmebaasis, et saaksite allpool toodud näidet testida. Kõigepealt loome tühja tabeli ühe veeruga:
CREATE TABLE users (
email varchar
);
2. Loome testi jaoks 10 000 kirjet:
DO $
BEGIN FOR i IN 1..10000 LOOP
INSERT INTO users values((select 'user' || i || '@example.com'));
END LOOP; END;
$;
Kasutame EXPLAIN ANALYZE'i, et kontrollida, kui kiiresti meie päringut töödeldakse, kui soovime leida andmebaasist konkreetse kasutaja.
EXPLAIN ANALYZE SELECT email FROM users WHERE email = 'user890example.com';
Meie päring sundis iteratsiooni kogu tabelis, et otsida meid huvitavat kirjet.
Seda protsessi nimetatakse järjestikuks skaneerimiseks. Sellisel juhul on kogu tabeli lugemine ja konkreetsete ridade välja filtreerimine parim viis töö tegemiseks.
PostgreSQL filtreerib välja mittevajalikud read ja tagastab lihtsalt need, mis meid huvitavad. See on tõesti parim, mida antud juhul teha. Järjekordne skaneerimine ei ole alati halb, on juhtumeid, kus järjestikune skaneerimine on ideaalne.
4. Nüüd on aeg kontrollida juba tehtud päringut tabelis, millel on INDEX UNIQUE. Seame indeksi ja täidame päringu.
EATE UNIQUE INDEX index_email on users(email);
EXPLAIN ANALYZE SELECT email FROM users WHERE email = 'user890example.com';
Seekord kasutas PostgreSQL ära indeksi skaneerimise, sest kõik vajalikud veerud on juba indeksis.
Ainult mõne rea valimine on indeksi kasutamisel väga tõhus. Kui aga valitakse rohkem andmeid, on indeksi ja tabeli skaneerimine liiga aeganõudev.
Kokkuvõte
Nagu näete, on indeksiga veeru päringu täitmisaeg palju lühem (näidatud näites on see langus 1,267 ms-lt 0,111 ms-le, seega koguni 91,24%!). Kõige olulisem erinevus on viis, kuidas PostgreSQL otsib meid huvitavat kirjet. Esimesel juhul pidi andmebaasimootor otsima kogu tabelist meile vajalikku kirjet. Teisel juhul on aga indeksistruktuur sorteeritud ja unikaalne, seega teadis mootor, kus kirje asub, mis kiirendas oluliselt päringu töötlemise aega.
Suurte andmebaaside ja väga keeruliste päringute puhul võivad õigesti seatud indeksid kiirendada oluliselt teie rakenduse tööd, ilma et oleks vaja suurendada selle masina kiirust, millel te andmebaasis otsingut teete.
Tasub meeles pidada, et indeksite loomine igale veerule ei ole hea tava. Loodud indeksid kiirendavad optimeerija tööd huvipakkuvate andmete otsimisel, kuid samas aeglustavad uute sisestamist ja olemasolevate uuendamist.