Oracle Berkeley DB and Relational Database Management Systems

Jadwiga Gnybek

O zaletach relacyjnych baz danych Oracle piszę od zawsze. Czas zatem na „odkrycie” świetnego nierelacyjnego produktu bazodanowego Oracle Berkeley DB. Oczywiście trudno by było przypisać Oracle’owi autorstwo tej bazy, niemniej jednak w chwili obecnej produkt ten dystrybuowany jest pod „naszym” logo.

Historycznie rzecz ujmując, pierwsze wersje tej bazy powstały jako efekt prac badawczych na Kalifornijskim Berkeley University, a dalszy rozwój tego projektu kontynuowano na Harvard University. Trzeba więc przyznać, że korzenie produkt ma iście królewskie. Funkcjonalne założenia leżące u podstaw jego budowy, to wysoce wydajna wbudowana baza danych (embeddable database engine), nie wymagająca uruchamiania odrębnego serwera i obsługi administratora bazy. Czyli: szybko, wydajnie, prosto, tanio.

Czym dziś jest Oracle Berkeley DB?

Berkeley DB jest na pewno produktem bardzo różnym od tego, co kojarzymy pod nazwą oraclowej bazy danych czyli bazą nierelacyjną i niedostępną dla zapytań SQL. A co jest wspólnego z produktami Oracle? Cechy charakteryzujące wszystkie „nasze” produkty, czyli wysoka wydajność, skalowalność i niezawodność.

Berkeley DB – będąc pomyślana jako baza danych współpracująca tylko z aplikacjami, dostępna jest jedynie poprzez API, nie wymaga za to postawienia oddzielnego bazodanowego serwera. Może dlatego właśnie programiści postrzegają ją raczej jako bibliotekę wspierającą przechowywanie danych, wyposażoną w świetny mechanizm indeksowania (B-trees) i haszowania. To z pewnością zbyt duże uproszczenie, ale przyznać należy, że algorytmy indeksujące są tu wyjątkowo skuteczne, nawet podczas wielowątkowego przetwarzania danych.

Rodzina Oracle Berkeley DB składa się dziś z trzech produktów:

Produkt nazwany na rysunku Berkeley DB jest napisaną w języku C biblioteką wspierającą wszelkie operacje dyskowe, która zapewnia ich transakcyjność oraz (nie wpływając ujemnie na wydajność) mechanizmy replikacji. Bibliotekę tę wykorzystać można pisząc aplikacje w C, C++ oraz językach skryptowych. Jeśli aplikacja wykorzystuje notacje XML, to programista może skorzystać ze zbudowanej „nad” Berkeley DB biblioteki Berkeley DB XML. Biblioteka ta potrafi parsować i indeksować dokumenty zapisane w notacji XML oraz porozumiewać się z językami C++, Java oraz językami skryptowymi. Jeśli zaś aplikacja napisana jest w języku Java, to programista dostaje do rąk jeszcze jeden produkt: Berkeley DB Java Edition. Jego funkcjonalność odpowiada produktowi Berkeley DB, a w środowisku Java Virtual Machine uruchamiana jest jako JAR.

Między systemem plików a RDBMS

No cóż, ponoć od przybytku głowa nie boli, nie zmienia to jednak faktu, że wybrać coś trzeba! Oczywiście budując duży system obsługiwany przez wielu użytkowników oraz przetwarzający tysiące transakcji, wybierzemy „klasyczny” serwer relacyjnej bazy danych. Dzięki temu będziemy mogli łatwo zintegrować nasze dane z innymi systemami informatycznymi lub uzupełnić naszą aplikację o brakujący moduł raportowy. Jeśli jednak pracujemy na plikach, tworzymy edytory, kompilatory czyli odczytujemy i zapisujemy dane z plików tekstowych, wówczas możemy zastosować zdecydowanie prostsze techniki składowania danych. Coś pomiędzy zwykłą obsługą plików, a RDBMS. Rozwiązania najprostsze czyli operacje zapisu i odczytu z plików płaskich są szybkie i łatwe w obsłudze. Niestety, ich użycie nie gwarantuje spójności danych w sytuacji jednoczesnego zapisu do tego samego pliku przez kilku użytkowników aplikacji. Inną niebezpieczną sytuacją jest fizyczne wyłączenie maszyny, na której właśnie realizowane są operacje zapisu. Nigdy nie wiemy, ile danych z tego pliku uda nam się odzyskać. Sytuację taką dobrze pamiętają najstarsi informatycy pracujący z bazami dBase. Oczywiście sytuacje takie nie mają prawa zaistnieć w relacyjnych bazach danych wyposażonych w motor RDBMS.

Co zatem zyskamy wybierając „złoty środek” czyli Berkeley DB? Przede wszystkim transakcyjność, spójność i mechanizmy odzyskiwania danych (recovery). Niestety, aby choćby przejrzeć zapisane tam informacje, niezbędne jest napisanie kilku linii kodu. Wszak Berkeley DB jest biblioteką i dzięki temu aplikacja nie traci czasu na komunikowanie się z motorem bazy danych. Wszystkie niezbędne do pracy aplikacji operacje mogą być wykonywane w ramach jednego procesu.

Jest to zapewne jedna z tajemnic dużej wydajności tego rozwiązania. Dokumentacja techniczna podaje bardzo obiecujące wyniki testów w tej kategorii – ponad dziewięćdziesiąt tysięcy insertów wprowadzonych do bazy w czasie jednej sekundy i ponad czterysta pięćdziesiąt odczytanych w sekundzie rekordów. Szybko, ale czy wygodnie? Berkeley DB to wynalazek programistów dla programistów, czyli bez kodowania ani rusz. Żaden tam SQL, ODBC czy inny wynalazek szatana; każdą operację na tej bazie danych wykonać trzeba za pośrednictwem odpowiedniego interfejsu API.

Na przykład aby utworzyć i otworzyć taką bazę wystarczy napisać:

static void
create_database(dbpp)
        DB **dbpp;
{
        DB *dbp;
assert(db_dreate(&dbp,NULL,0)==0)
        assert(dbp->open(dbp,NULL, DBNAME, NULL, DB_TREE, DB_CREATE, 0644) == 0);
        *dbpp = dbp
}

W sumie – porównując to do składni polecenia CREATE DATABASE, wcale tego kodu nie jest tak dużo. Nieco bardziej skomplikowanie wyglądać będą natomiast odpowiedniki operacji „select” i „insert”. Jeśli chcemy coś zapisać do Berkeley DB musimy uruchomić interfejs do_put:

static void
do_put(dbp, n)
        DB *dbp;
        int n;
{
char key[KEYLEN];
        DBT datadbt, keydbt;
        int i;
        stmcpy(key, FIRST_KEY, KEYLEN);
        memset(&keydbt, 0, sizeof(keydbt));
keydbt.data = key;
keydbt.size = KEYLEN;
        for (i=0; i<n; i++)
        {
        assert(dbp->put(dbp,NULL, &keydbt, 0)==0);
next_key(key);
        }
}

Pobranie danych z bazy wymaga równie dużo zachodu:

static void
do_put(dbp, n)
        DB *dbp;
{
DBC *dbc;
char key[KEYLEN];
        DBT keydbt, datadbt;
        int n,ret;
assert(dbp->cursor(dbp,NULL, &dbc, 0)==0);
        memset(&keydbt, 0, sizeof(keydbt));
        memset(&datadbt, 0, sizeof(datadbt));
        n=0
        TIMER_START;
while((ret = dbc->get(dbc, &keydbt, datadbt, DB_NEX))==0)
{
n++;
}
TIMER_STOP;
TIMER_DISPLAY(n);
}

I tak dalej, z każdą kolejną operacją. Trochę żmudne, prawda? Pewnie dlatego na Berkeley DB nabudowano Berkeley DB XML, które umożliwia wykorzystanie XPATH i XQuery. Jeśli nasza baza przechowuje dokumenty, jest to chyba bardzo sensowna propozycja. Co ważne, wykorzystanie tego interfejsu nie zamyka nam możliwości składowania w jednej bazie zarówno danych sformatowanych jako XML, jak i nie posiadających tej struktury.

Do czego Berkeley DB?

Skoro jesteśmy przy strukturze danych, zajrzyjmy do środka Berkeley DB. W odróżnieniu od uporządkowanych matematycznym modelem baz relacyjnych, struktura tej bazy w stu procentach zależna jest od potrzeb współpracującej z nią aplikacji. Jest to kolejna z tajemnic wysokiej wydajności tego rozwiązania. Rekord Berkeley DB składa się z dwóch elementów: klucza i porcji danych, a każdy z tych elementów to ciąg znaków o określonej lub zmiennej długości. Wyszukiwanie informacji odbywa się zatem po wartościach kluczy, bez wnikania w strukturę danych odpowiadających temu kluczowi.

Taki sposób składowania danych najlepiej współpracuje z aplikacjami bazującymi na zapytaniach statycznych. Tu właśnie baza ta znajduje najwłaściwsze zastosowanie. Począwszy od telefonów komórkowych, na serwerach poczty skończywszy – bazy Berkeley DB mogą być całkiem małe i zupełnie duże. Oczywiście w zależności od potrzeb i posiadanych zasobów, bazy te konfigurować można wieloma parametrami. Parametryzowane są na przykład: rozmiary strony pamięci dla poszczególnych tablic, polityka zarządzania buforami, mechanizmy detekcji i usuwania zakleszczeń danych, zasady zapewniania spójności danych, reguły zapisu synchronicznego i replikacji danych, itd.

Do czego warto zastosować tę bazę? Jest już wiele dziedzin, które z powodzeniem wykorzystują te biblioteki, więc warto przyjrzeć się temu portfolio. Zacznijmy od open source’owych rozwiązań Identity Management. Ich podstawowym zadaniem jest przechowywanie nazw użytkowników, ich haseł dostępowych, historii ich aktywności lub wprowadzonych preferencji. Do takich właśnie danych łatwo zdefiniować statyczne zapytania wybierające potrzebny zestaw informacji z pliku płaskiego. Berkeley DB dostarczyć może tu zatem wymaganej szybkości i pewności działania bez zbędnego bagażu niewykorzystywanych funkcjonalności. Innym równie szerokim i odpowiedzialnym zastosowaniem tej bazy są serwery transferu informacji, czyli wszelkiego rodzaju serwery mailowe i czatowe czy SMSowe. Tu też prosta struktura danych wymaga jedynie zachowania spójności danych, co zapewnia dużą wydajność. Podobne wymagania mają dane wykorzystywane do zarządzania powierzchnią dyskową czyli Storage systems. Ich zadaniem jest przechowywanie informacji o nośnikach i sposobach ich zapełnienia tak, aby operacje I/O realizowane były szybko nawet w sytuacjach, gdy zapis danych na dysk obarczony jest realizacją wytycznych zadeklarowanego poziomu RAID. Dodatkową funkcjonalnością takiego oprogramowania jest również zarządzanie wydajnym wykorzystaniem posiadanej powierzchni dyskowej i maskowanie jej fizycznej struktury przed użytkownikiem systemu plików. Bazy oparte o bibliotekę Berkeley DB wykorzystywane są również w oprogramowaniu aktywnych urządzeń sieciowych oraz mechanizmach tworzących logi i pliki śladu.

Jak widać, portfolio to wygląda całkiem imponująco. Nie ma tu może nazw znanych systemów informatycznych wspierających biznes, ale za to znajdziemy z pewnością oprogramowanie narzędziowe, bez którego trudno wyobrazić sobie dziś profesjonalną infrastrukturę informatyczną.