Olen jatkanut tutustumista NoSQL-tietokantoihin (tai vaihtoehtoisesti "AltDB" -- tämä nimi ei ole vielä oikein ottanut tuulta). Eräs yksinkertaisimmista ja tehokkaimmista on Tokyo Tyrant, joka muodostuu oikeastaan kahdesta palasesta:
- Tokyo Cabinet on C-kielinen tietokantakirjasto.
- Tokyo Tyrant on tietokantapalvelin, joka käyttää Tokyo Cabinetia.
Tokyo Cabinet vastaa idealtaan Berkeley DB -kirjastoa, jonka voi niinikään upottaa omaan sovellukseensa. Tokyo Cabinet on tiettävästi äärimmäisen tehokas, pystyen yli miljoonaan operaatioon sekunnissa.
Tokyo Tyrant lisää Cabinetin päälle verkkokerroksen, joten se on luonnollisesti hitaampi. Kehittäjän oman esityksen mukaan Tyrant pystyy noin 58000 operaatioon sekunnissa. Nämä luvut riippuvat tietysti palvelinraudasta.
Tokyo Cabinet on pohjimmiltaan samanlainen key-value-store kuin esimerkiksi Memcached. Jokaista yksilöivää avainta vastaa tasan yksi arvo. Arvo voi olla esimerkiksi JSON-objekti, joka on koodattu merkkijonoksi. Avain puolestaan on useimmiten UUID, johon on kenties lisätty prefiksi, jonka avulla kannasta voi tarvittaessa seuloa esiin tietyn tyyppisiä rivejä.
Rivien seulominen esiin avainprefiksien avulla ei ole kuitenkaan tehokasta. Key-value-tietokantojen ideana on rakentaa relaatiot ennemmin niin, että yhden avaimen alta löytyy lista toisista avaimista. Esimerkkinä voisi olla tällainen rakenne:
- User:kennu
- b714dc6fe2304e8d9160b5f389959a8a
- UserInfo:b714dc6fe2304e8d9160b5f389959a8a
- { "realname":"Kenneth Falck" }
- UserBlogs:b714dc6fe2304e8d9160b5f389959a8a
- Blog:eded0f3009174fbcaa2da50c42c7468d Blog:51d49d44f4b64fe4827edc9caba0a619
- Blog:eded0f3009174fbcaa2da50c42c7468d
- { "name": "Tekniikkablogi" }
- Blog: 51d49d44f4b64fe4827edc9caba0a619
- { "name": "Hupiblogi" }
Tässä mallissa käyttäjätietoja lähdetään selvittämään etsimällä ensin avain User:(tunnus), joka kertoo käyttäjän UUID:n. Sillä voidaan sitten etsiä UserInfo:(uuid), josta saadaan käyttäjän perustiedot, sekä UserBlogs:(uuid), josta löytyy luettelo käyttäjän blogien UUID:istä välilyönneillä eroteltuna. Lopuksi noudetaan vielä kunkin blogin tarkemmat tiedot avaimella Blog:(uuid).
Haaste tällaisessa relaatiomallissa on säilyttää integriteetti UserBlogs- ja Blog-entiteettien välillä. Kun näitä kenttiä muokataan, muutosten tulisi olla atomisia, jotta tietokantaan ei jää roikkumaan Blog-rivejä, joihin ei enää viitata UserBlogs-kentistä tai toisin päin. Näin voi käydä, jos verkkoyhteys katkeaa sopivassa kohdassa tai sovelluksessa tapahtuu jokin muu virhe operaatioiden välillä.
Tokyo Tyrantissa atomiset operaatiot voi toteuttaa Lua-laajennuksilla. Palvelimelle annetaan käynnistyksen yhteydessä Lua-tiedosto, jossa määritellään laajennusoperaatiot funktioina. Blogiesimerkin tapauksessa laajennus voisi olla tällainen:
-- key: Blog key
-- value: User key + '|' + Blog value
function addblog(key, value)
-- Split value into userkey and blogvalue
parts = _split(value, '|')
userkey = parts[1]
blogvalue = parts[2]
-- Create the Blog
if not _put(key, blogvalue) then
return nil
end
-- Add Blog's key to UserBlogs of owner
akey = key
if _vsiz(userkey) > 0 then
akey = ' ' .. akey
end
if not _putcat(userkey, akey) then
_out(key)
return nil
end
return value
end
Laajennuskoodi on ehkä vähän sekavan tuntuista, mutta se hoitaa asiansa. Nyt sovellus voi kutsua addblog-laajennusta, joka luo ensin blogin ja lisää sitten sen avaimen käyttäjän blogiluetteloon välilyönnillä eroteltuna. Virhetilanteessa blogi poistetaan (_out), jottei se jää roikkumaan tietokantaan.
Vastaavasti olisi suhteellisen helppoa koodata deleteblog-laajennus, joka poistaa blogin sekä sen avaimen käyttäjän blogiluettelosta. Oikeassa sovelluksessa erottimena voisi olla mieluummin esimerkiksi NUL-merkki eikä '|'. Lisäksi operaation ympärille pitäisi lisätä _lock()- ja _unlock()-kutsut, joilla lukitaan sekä key että userkey koko operaation ajaksi.