Bevezetés
Mnesia egy olyan elosztott adatbázis-kezelő rendszer(DBMS), amely alkalmas telekommunikációs alkalmazások és más folyamatos végrehajtású Erlang
alkalmazások számára. Az alábbiakban felsorolok néhány igen fontos és vonzó lehetőséget, amelyekkel az Mnesia rendelkezik:
- Relációs/objektum hibrid adatmodell, amely telekommunikációs alkalamzásokhoz alkalmas.
- Célirányosan tervezett DBMS lekérdező nyelv, QCL.
- Perzisztencia: A táblák egységesen tárolhatóak mind a lemezen mint a központi memóriában.
- Replikáció: A táblák replikálhatóak különböző csomópontokban.
- Atomi tranzakciók: Táblamanipuláló műveletek sorozata csoportosítható egyetlen atomi tranzakcióban.
- Elhelyekedési átlátszóság: Az adatok tényleges elhelyezkedésének tudta nélkül írhatóak programok.
- Rendkívül gyors, valós-idejű keresés az adatokban.
- Séma minupuláló rutinok: Lehetőség van újrakonfigurálni futási időben a DBMS-t a rendszer megállítása nélkül.
Táblák
Shop
Name |
Quantity |
Cost |
apple |
20 |
2.3 |
orange |
100 |
3.8 |
pear |
200 |
3.6 |
banana |
420 |
4.5 |
potato |
245 |
1.2 |
Cost
Name |
Price |
apple |
1.5 |
orange |
2.4 |
pear |
2.2 |
banana |
1.5 |
potato |
0.6 |
Ahhoz, hogy ezeket a táblákat Mnesiaban reprezentálni tudjuk, szükségünk van rekordokra, amelyek az egyes oszlopokat definiálják. Tehát:
-record(shop, {name, quantity, cost}).
-record(cost, {name, price}).
A rekordokban n-eseket tárolunk. Az n-nes egyik elemét (alapértelmezettként az elsőt) a tábla kulcsának nevezzük. A táblák fajtái:
- set: ahol a kulcs értéke egyedi
- ordered set: olyan set, amelyben a sorok a kulcsérték szerint vannak rendezve
- bag: a különböző n-esek rendelkezhetnek azonos kulccsal, de két n-nes teljesen nem lehet egyforma
- duplicate bag: olyan bag, amelyben ugyanazon n-nes akárhányszor előfordulhat
Select * From
A kód, amellyel lekérdezhetjük a Shop tábla összes sorát a következő. (Minden kódrészlet egy kommenttel kezdődik, amely az Erlang utasítással
ekvivalens SQL utasítást tartalmazza.)
%% SELECT * FROM SHOP;
demo(select_shop) -> do(qlc:q([X || X <- mnesia:table(shop)]));
A lekérdezés központi része a qlc:q hívás, amely lekérdezést lefordítja egy olyan belső formára, amelyet adatbázis lekérdezésekhez használunk.
A lekérdezést a do() nevű függvénynek adjuk, amelynek a lekérdezés végrehajtása és az eredmény visszaadása a feladata.
do(Q) ->
F = fun() -> qlc:e(Q) end,
{atomic, Val} = mnesia:transaction(F), Val.
Futtatva a programot az alábbiak szerint kapjuk:
1> test_mnesia:start().
ok
2> test_mnesia:reset_tables().
{atomic,ok}
3> test_mnesia:demo(select_shop).
[{shop,orange,100,3.80000},
{shop,pear,200,3.60000},
{shop,banana,420,4.50000},
{shop,potato,2456,1.20000},
{shop,apple,20,2.30000}]
Megjegyzés: a tábla sorai tetszőleges sorrendben jelenhetnek meg.
Vetítés
A következő lekérdezéssel kérdezhetjük le a name és a quantity oszlopokat a Shop táblából:
%% SQL equivalent
%% SELECT item, quantity FROM shop;
demo(select_some) ->
do(qlc:q([{X#shop.item, X#shop.quantity} || X <- mnesia:table(shop)]));
4> test_mnesia:demo(select_some).
[{orange,100},{pear,200},{banana,420},{potato,2456},{apple,20}]
Megjegyzés:
- X#shop.name a Shop rekord name oszlopára hivatkozik
- X#shop.quantity a Shop rekord quantity oszlopára hivatkozik
Sor hozzáadása
A következő kódrészlet létrehoz egy új shop rekordot és beszúrja az a táblába:
add_shop_item(Name, Quantity, Cost) ->
Row = #shop{item=Name, quantity=Quantity, cost=Cost},
F = fun() ->
mnesia:write(Row)
end,
mnesia:transaction(F).
4> test_mnesia:add_shop_item(orange, 236, 2.8).
{atomic,ok}
%% list the shop table again so we can see the change
5> test_mnesia:demo(select_shop).
[{shop,orange,236,2.80000},
{shop,pear,200,3.60000},
{shop,banana,420,4.50000},
{shop,potato,2456,1.20000},
{shop,apple,20,2.30000}]
Megjegyzés: A shop tábla kulcsa az első oszlop. Ha olyan sort szúrunk be, amelynek az elsődleges
kulcsa már megtalálható a táblában, akkor annak értékei felülíródnak. Ha az elsődleges kulcs még nem
létezett, akkor létrejön egy új sor az adatbázisban.
Sor törlése
Ahhoz a tábla egy sorát töröljük, szükség van a sor object ID-jára(OID). Ez a tábla nevéből és az elsődleges kulcs értékéből áll.
remove_shop_item(Item) ->
Oid = {shop, Item},
F = fun() ->
mnesia:delete(Oid)
end,
mnesia:transaction(F).
Feltételes lekérdezés
A következő példában lekérdezzük azokat a termékek nevét, amelyeknek a darabszáma kisebb, mint 250.
%% SELECT shop.nev FROM shop
%% WHERE shop.quantity < 250;
demo(reorder) ->
do(qlc:q([X#shop.name || X <- mnesia:table(shop), X#shop.quantity < 250]));
5> test_mnesia:demo(reorder).
[orange,pear,apple]
Összekapcsolás
Most kérdezzük le azon termékek nevét, amelyeknek a mennyisége kevesebb, mint 250 és az ára kevesebb, mint 2.0.
Ehhez szükség van a két tábla összekapcsolására, amit az alábbiak szerint tehetünk meg.
%% SELECT shop.name, shop.quantity, cost.name, cost.price
%% FROM shop, cost
%% WHERE shop.name = cost.name
%% AND cost.price < 2
%% AND shop.quantity < 250;
demo(join) ->
do(qlc:q([X#shop.nev || X <- mnesia:table(shop),
X#shop.quantity < 250,
Y <- mnesia:table(cost),
X#shop.name =:= Y#cost.name,
Y#cost.price < 2])).
6> test_mnesia:demo(join).
[alma]
Tranzakciók
A tranzakciókezelés a hibatűrő és elosztott rendszerek tervezésének hatékony eszköze.
Az Mnesia tranzackió egy olyan mechanizmus, melynek segítségével adatbázis műveletek sorozata hajtható végre egyetlen
funkcionális egységként. A tranzakcióként futtatott funkcionális blokkot Funkcionális Objektumnak(Fun) nevezzük, és ez a kód
a rekordokat írni, olvasni és törölni tudja.
Tranzakciókezelés során az alábbi tulajdonságokat kell betartani:
- A tranzakció lekezelőjének biztosítania kell, hogy a tranzakción belül elhelyezett Fun utasításainak végrehajtása során
másik tranzakció műveleteit ne zavarja.
- A tranzakció lekezelőjének biztosítania kell, hogy vagy a tranzakció minden utasítása atomi módon sikeresen végrehajtódik
vagy ha a végrehajtás sikertelen, akkor a gondoskodni kell a végrehajtások visszavonásáról.
- A tranzakciók 4 fontos tulajdonsággal rendelkeznek, ezek: Atomosság, Konzisztencia, Elkülönültség, Tartósság.
Zárolások
Az Mnesia az elkülöntség tulajdonság betartását hagyományos módon, két-fázisú zárolással valósítja meg. Ez azt jelenti, hogy egy
rekordot annak olvasása vagy írása előtt zárolni kell. Mnesiában 5 féle zárolási módot különböztetünk meg:
- olvasási zár: egy rekord replikátumának olvasása esetén használjuk
- írási zár: ha egy tranzakció egy rekordot szeretne módosítani, akkor az adott rekord minden replikátumára írási zárat kell rakni
- táblaolvasási zár: akkor használatos, amikor tranzakció egy bizonyos tulajdonságot kielégítő rekordot keresve végigiterál az egész táblán
- táblaírási zár: akkor használatos, amikor egy tranzakció egyszerre a tábla több rekordját módosítja.
- ’sticky’ zár: olyan írási zár, amely a zárolást elindító tranzakció terminálása után a nodeot helyben hagyja.
Mnesia a zárolások kiadását és felszabadítását automatikusan elvégzi.
Táblák zárolásának két módja van
- mnesia:read_lock_table(Tab): Tab táblára olvasási zárat rak
- mnesia:write_lock_table(Tab): Tab táblára írási zárat rak
Alternatív megadási módok
mnesia:lock({table, Tab}, read)
mnesia:lock({table, Tab}, write)
Piszkos műveletek
Számos alkalmazás esetén a tranzakciók végrehajtása teljesítménycsökkenéshez vezethet. A piszkos műveletek olyan rövidítéseknek tekinthetők, amelyek a
feldolgozás bizonyos részét mellőzve a tranzakció végrehajtásának sebességét növelik. Ezért az Mnesia tartalmaz olyan beépített függvényeket, amelyek
tranzakciók használata nélkül módosítják a táblák tartalmát. Persze számos hátránya van: az atomosság és az elkülönültség tulajdonságai elvesznek.
Minden ilyen művelet hibás futás esetén meghívja az exit({aborted, Reason}) függvényt. Az Mnesia az alábbi műveleteket biztosítja számunkra. Ezen műveletek
tranzakcióban történő végrehajtás esetén sem igényelnek zárolást.
- mnesia:dirty_read({Tab, Key}): rekordok olvasása
- mnesia:dirty_write(Record): Record rekord tartalmát írja
- mnesia:dirty_delete({Tab, Key}): rekordok törlése a megadott Key kulcsértékek segítségével
- mnesia:dirty_delete_object(Record): delete_object/1 függvény alternatívája
- mnesia:dirty_first(Tab: A Tab tábla első kulcsértékét adja vissza
- mnesia:dirty_next(Tab, Key): A Tab tábla következő kulcsértékét adja vissza. Ez a függvény lehetővé teszi, hogy a tábla tartalmának rendezettségét megfordítsuk és a tábla minden rekordján végrehajtsunk valamilyen műveltet. A tábla végét a ’$end_of_table’ kulcs jelzi.
- mnesia:dirty_slot(Tab, Slot): A megadott Slottal rendelkező rekordokat adja vissza. A dirty_next/2-höz hasonló módon használható a tábla rendezettségének megfordítására
- mnesia:dirty_match_object(Pat): mnesia:match_object/1 függvény piszkos megfelelője
- mnesia:dirty_select(Tab, Pat): mnesia:select/2 függvény piszkos megfelelője
- mnesia:dirty_index_match_object(Pat, Pos): mnesia:index_match_object/2 függvénnyel ekvivalens piszkos függvény
- mnesia:dirty_all_keys(Tab): az mnesia:all_keys/1 függvény piszkos megfelelője