Brezplačni e-vodič: Kako postati programer?
Poglej več
Odličen uvod v programiranje: SPOMLADANSKI TEDEN PROGRAMIRANJA ZA 99 EUR!
Na tečaj

Optimiziranje indeksov v Google Datastore

Naslov te blog objave zveni hudo kompliciran, vendar zadeva dejansko ni tako grozna 🙂

V tej objavi bomo govorili o tem, kako delujejo indeksi v podatkovni bazi Datastore na Google App Engine (GAE) oz. Google Cloudu, kaj je najpogostejša napaka začetnikov pri uporabi indeksov ter kako to napako odpraviti.

Komu je ta objava namenjena?

Objava je najbolj primerna za tiste, ki že poznate osnove GAE. Če teh izkušenj še nimaš oz. sploh nimaš izkušenj s programiranjem, pa bi se rad to naučil, si poglej naš popularen Web development tečaj tu: https://smartninja.si/courses.

Datastore indeksi

Kot prvo moramo razčistiti, kaj v Datastore kontekstu pomenijo indeksi. Indekse verjetno poznaš iz relacijskih podatkovnih baz, kjer jih narediš za lažji search po podatkovni bazi. Ni pa nujno, da jih imaš in po bazi lahko iščeš tudi brez njih. Pri datastoru je zadeva drugačna. Brez indexov po Datastoru ne moreš delati queryjev. Datastore namreč ni relacijska podatkovna baza, ampak je t.i. NoSQL baza in shranjuje podatke v t.i. ključ-vrednost obliki (key – value).

Recimo, da imamo model User (napišimo ga kar v Pythonu):

class User(ndb.Model):
    first_name = ndb.StringProperty()
    last_name = ndb.StringProperty()
    email = ndb.StringProperty()
    phone = ndb.StringProperty()
    description = ndb.TextProperty()
    active = ndb.BooleanProperty(default=False)

Takoj, ko začnemo vnašati prve uporabnike v našo podatkovno bazo, bo Datastore avtomatično ustvaril indekse za praktično vse atribute v modelu. Tako bo naredil en indeks za first_name atribut, drugega za last_name, nato pa še za email, phone in active.

Čakaj, kaj pa indeks za description?

Atribut description je tipa TextProperty, za katerega Datastore avtomatično ne naredi indeksa. Datastore prav tako ne naredi indeksov za nekaj drugih tipov, kot npr. JsonProperty, vendar pa za veliko večino tipov indeks naredi (preberi si več o tem na tej povezavi).

Kaj to pomeni?

Po eni strani nam dejstvo, da ima nek atribut svoj indeks, pomaga iskati (query) po Datastoru. Na primer, če bi želeli najti vse uporabnike, ki se pišejo Novak, bi napisali tak query:

novakovi = User.query(User.last_name == "Novak").fetch()

Po drugi strani pa indeksi vzamejo prostor v našem Datastoru. Prostor, ki vedno bolj obremenjuje našo denarnico, ko naša aplikacija raste in preseže Googlovo (sicer zelo dobrodušno) zastonj kvoto (t.i. free quote).

Datastore v bistvu ne naredi le enega indeksa za vsak atribut v našem modelu, ampak kar dva! Zakaj dva? Zato, ker je en indeks sortiran naraščajoče (ASC oz. ascending), drugi pa padajoče (DESC oz. descending).

Rešitev seveda je, da število indeksov omejimo. Po vsakem atributu, ki ga imamo v modelu, namreč ne bomo delali queryja, zato nima smisla, da se za tak atribut dela indeks.

Omejevanje količine indeksov

Pri vsakem atributu v našem modelu lahko določimo, ali naj se zanj naredi index, ali ne. To naredimo tako, da mu določimo lastnost (property) indexed, ki ima le dve možnosti: True ali False. Če je True, potem bo zanj narejen index, če pa False, pa ne.

class User(ndb.Model):
    first_name = ndb.StringProperty(indexed=False)
    last_name = ndb.StringProperty(indexed=True)
    email = ndb.StringProperty(indexed=True)
    phone = ndb.StringProperty(indexed=False)
    description = ndb.TextProperty(indexed=False)
    active = ndb.BooleanProperty(default=False, indexed=True)

Za vsak atribut se je treba vprašati, ali se bo po njem kdaj izvajalo query ali ne. Jaz sem se odločil, da bodo indeksi narejeni za atribute last_name, email ter active, za ostale pa ne.

Dobra praksa: čeprav pri določenih atributih ne bi rabil napisati indexed lastnosti, pa bi vseeno delovalo tako, kot bi želel, je to vseeno dobro narediti. Na primer, atribut last_name je tipa StringProperty, ki je že v osnovi indeksiran, zato mi ne bi bilo treba napisati indexed=True. Vseeno pa je dobro, da se to vpiše, saj je tako bolj jasno, za katera polja imamo indekse in za katera ne.

Kaj pa, če že imam narejen model ter vnose v Datastore, in bi želel naknadno dodati indexed lastnost mojim atributom?

Ni problema, indexed lastnost lahko dodaš tudi naknadno. Veljala bo pa za vse nadaljne vnose. Torej, če želiš nekemu atributu, ki je tipa StringProperty in je bil avtomatično indeksiran, dodati lastnost indexed=False, se indeks za ta atribut ne bo več polnil. Prav tako pa več ne bo možno delati queryja po tem atributu.

Kaj pa, če imam za nek atribut nastavljen indexed=False, pa si nekoč v prihodnosti premislim in ga želim imeti indeksiranega?

Tudi to je možno, vendar pa bo zahtevalo nekaj dodatnega dela. Vzemimo spet naš primer modela User. V njem imamo atribut first_name, ki ima lastnost indexed=False, kar pomeni da ni indeksiran. Recimo, da smo si premislili, in ga želimo imeti indeksiranega, zato da bomo lahko delali query po njem. Najprej mu moramo nastaviti indexed=True:

class User(ndb.Model):
    first_name = ndb.StringProperty(indexed=True)
    last_name = ndb.StringProperty(indexed=True)
    email = ndb.StringProperty(indexed=True)
    phone = ndb.StringProperty(indexed=False)
    description = ndb.TextProperty(indexed=False)
    active = ndb.BooleanProperty(default=False, indexed=True)

To, kar smo zdaj naredili, pomeni, da bodo vsi nadaljni vnosi novih uporabnikov bili indeksirani.

Ampak kaj pa obstoječi vnosi?

Obstoječe vnose pa je treba posodobiti. To naredimo tako, da iz baze potegnemo vse obstoječe vnose tipa User ter jih preprosto še enkrat vnesemo notri:

users = User.query().fetch()

for user in users:
    user.put()

Vendar pozor: v kolikor imaš že zelo veliko vnosov uporabnikov v bazi, zna ta query vzeti nekaj časa (ter posledično denarja). Performans Datastora je namreč vezan na to, koliko vnosov želimo dobiti ven iz baze, ne pa na število obstoječih vnosov v bazi (to je pomembno vedeti!).

Zatorej nastavi indexed lastnosti pri vseh svojih atributih v modelih že takoj, ko narediš model, da se izogneš kakim neprijetnim težavam v prihodnosti.

(V tej objavi smo govorili le o t.i. built-in indeksih. Obstajajo pa tudi t.i. kompozitni indeksi, ki pa so dovolj obsežna tema, da bi rabili za njih posebno blog objavo. In mogoče enkrat to tudi bo – spremljaj naš blog in se prijavi na naše e-novice!) 🙂

Nadaljuj z branjem