Struktura fizyczna bazy danych (InterBase i Firebird)
Alexey Kovyazin, Sergey Vostrikov, ostatnia aktualizacja 05-czerwca-2004
Fizyczna struktura bazy danych
Dlaczego musimy studiować fizyczną strukturę bazy danych InterBase?
Zazwyczaj, gdy mówimy o fizycznej strukturze bazy danych InterBase, mamy na myśli to, że reprezentuje ona dane z punktu widzenia organizacji danych na niskim poziomie - aż do poziomu bajtów. Wielu programistów tworzących aplikacje przy użyciu języków wysokiego poziomu zaniedbuje studiowanie szczegółów niskiego poziomu. Jednak znajomość głównych zasad organizacji danych wewnątrz bazy danych jest kluczem do efektywnego projektowania aplikacji bazodanowych. Dlatego zrobimy ekskurs w głąb organizacji bazy danych InterBase i dowiemy się, jak jest ona urządzona.
Do czego więc służy system zarządzania bazą danych (DBMS)? Oczywiście do przechowywania i kontrolowania danych. Brzmi to banalnie, ale warto się nad tym zastanowić. Użytkownik przekazuje dane do DBMS, który w jakiś sposób tłumaczy te dane na zrozumiałe dla siebie formaty wewnętrzne. Możesz sobie wyobrazić „0 i 1”, jeśli słowa „wewnętrzny format danych” sprawiają pewne trudności ze skojarzeniami. DBMS przechowuje te dane i w momencie pierwszego żądania musi je wydobyć ze swojego formatu, przekonwertować do odpowiedniego widoku i przekazać użytkownikowi.
Tematem tego rozdziału jest to, jak DBMS przechowuje swoje dane, w jakim widoku i jak są one zorganizowane na najniższym poziomie. Postaramy się wyjaśnić, jak z bitów i bajtów leżących na dysku twardym otrzymujemy wartościowe dane.
Pliki bazy danych InterBase
Zazwyczaj, gdy mówimy o bazie danych, mamy na myśli sam DBMS oraz informacje użytkownika, a nawet programy klienckie, które pracują z danymi. W tym rozdziale będziemy rozważać bazę danych jako pliki bazy danych.
Baza danych InterBase reprezentuje jeden lub kilka plików zawierających informacje o wszystkim, co jest związane z tą bazą. Wyjątkiem są informacje o użytkownikach, ponieważ użytkownicy są definiowani na poziomie całego serwera i są przechowywani osobno, w bazie bezpieczeństwa admin.ib (w wersjach przed 7 był to ISC4.GDB).
Wskazówka: Zajrzyj do rozdziału „Bezpieczeństwo serwera i bazy danych”, aby dowiedzieć się więcej o zasadach bezpieczeństwa InterBase.
Tak więc wszystkie informacje o bazie danych są przechowywane w tych plikach: same dane, indeksy, wyzwalacze, procedury składowane itp.
Baza danych InterBase dla przeciętnego projektu reprezentuje jeden plik, ponieważ nowoczesne wersje InterBase mogą używać 64-bitowego wejścia/wyjścia do operowania na pliku danych, co daje możliwość posiadania pliku danych o rozmiarze do 64 GB. Wcześniejsze wersje InterBase miały ograniczenie 4 gigabajtów na każdy plik bazy danych (do 64 TB dla całej bazy danych). Jak możemy przypuszczać, 64 gigabajty w zupełności wystarczą do przechowywania informacji prawie każdej aplikacji bazodanowej. Ale jeśli to konieczne, możemy podzielić bazę danych na kilka plików. Nawiasem mówiąc, istnieją bazy danych InterBase o rozmiarach setek gigabajtów.
IBSurgeon - przewodnik po bazie danych InterBase
Musimy poznać szczegółowo strukturę plików bazy danych InterBase. Dlatego pożądane jest posiadanie wygodnego narzędzia, które pozwala pracować bezpośrednio z plikami bazy danych, nie za pomocą jądra serwera InterBase. Najłatwiejszym sposobem jest użycie zwykłego edytora szesnastkowego i próba zrozumienia struktury plików bazy danych, patrząc na ich reprezentację HEX. Byłaby to dość żmudna praca.
Ale na szczęście istnieje narzędzie do bezpośredniej pracy z bazami danych InterBase. Jest to IBSurgeon Editor - narzędzie do bezpośredniej pracy na niskim poziomie z bazami danych InterBase, które może być używane do studiowania wewnętrznej struktury baz danych InterBase oraz diagnozowania uszkodzonych baz danych w celu ich odzyskania. Więcej szczegółów znajdziesz w dodatku „Narzędzia administratora i programisty InterBase”.
IBSurgeon używa własnego alternatywnego mechanizmu dostępu do bazy danych, który pozwala otwierać i przeglądać bazy danych w dowolnym stanie, w tym mocno uszkodzone, których nie może otworzyć jądro serwera InterBase/FireBird/Yaffil.
Będziemy używać IBSurgeon do zilustrowania wewnętrznej struktury bazy danych.
Pliki *.IB/*.FDB od środka
IB to rozszerzenie zalecane dla plików bazy danych InterBase, a FDB dla Firebird (wcześniej było to GDB). Pierwszą rzeczą, którą musimy powiedzieć o strukturze pliku IB, jest to, że reprezentuje on zbiór stron o ściśle określonym rozmiarze. Rozmiar pliku bazy danych jest podzielny przez rozmiar strony, który jest niezmienny dla wszystkich plików tej bazy danych. Różne wersje InterBase obsługują różne rozmiary stron, co pokazano w tabeli 1. Rozmiar strony jest ustawiany podczas tworzenia bazy danych i nie może być zmieniony w trakcie jej cyklu życia. Innymi słowy, możemy zmienić rozmiar strony tylko podczas przywracania bazy danych z kopii zapasowej.
Tabela 1. Rozmiary stron obsługiwane przez różne wersje InterBase
| Wersja InterBase | Rozmiar strony, bajty | ||||
| 1024 | 2048 | 4096 | 8192 | 16384 | |
| InterBase 4.0 | * | * | * | * | |
| InterBase 5.x | * | * | * | * | |
| InterBase 6.x-7.x | * | * | * | * |
Odczyt i zapis danych w bazie danych są wykonywane strona po stronie; wiele ważnych cech serwera i bazy danych, takich jak rozmiar pamięci podręcznej bazy danych, zależy od rozmiaru strony i jest liczone w „stronach”.
Otwórzmy dowolną bazę danych InterBase za pomocą IBSurgeon. Wystarczy kliknąć dwukrotnie plik bazy danych. Obrazek 1 przedstawia listę stron, która pojawia się po otwarciu bazy danych przez IBSurgeon:

Obrazek 1. Lista stron bazy danych
Strony mogą być różnych typów, z których każdy służy określonemu celowi. Współzależności różnych typów są warunkowo przedstawione na obrazku 2. Obrazek 2 schematycznie przedstawia rozmieszczenie stron w pliku bazy danych - od lewej do prawej, z góry na dół, licząc od początku pliku. Strony tego samego typu nie występują ściśle jedna po drugiej - mogą być swobodnie wymieszane, rozmieszczone w pliku w kolejności, w jakiej zostały utworzone przez serwer podczas rozszerzania lub tworzenia baz danych.

Obrazek 2. Współzależności między różnymi typami stron w bazie danych InterBase
Musiałeś zauważyć, że niektóre typy stron nie mają odniesień do innych typów stron. Nie ma tu jednak sprzeczności - chodzi o to, że te typy stron są połączone i używane na innym poziomie strukturalnym. Mogą być powiązane z tabelą RDB$PAGES i innymi tabelami systemowymi (tę tabelę i inne obiekty systemowe rozważymy poniżej - w rozdziale „Logiczna struktura bazy danych”). Na obrazku 2 widzimy tylko jawne odniesienia między stronami na poziomie fizycznym.
Rozważmy szczegółowo, jakie typy stron istnieją w bazie danych InterBase. W pliku ods.h z zestawu kodów źródłowych InterBase znajdują się informacje o wszystkich możliwych typach stron. Będziemy często odwoływać się do tego pliku, aby otrzymać dane nie tylko o ODS, ale także o wielu innych fundamentalnych rzeczach jądra InterBase w oryginalnym źródle. W sumie zadeklarowanych jest 11 typów stron, ale tylko 9 z nich warto wyjaśnić (co wyraźnie widać z tabeli 2). Typy stron z identyfikatorami 0 i 1 są niezdefiniowane lub nieużywane.
Tabela 3. Typy stron we FB
| Definicja w ods.h | Identyfikator typu strony | Opis strony |
| pag_undefined | 0 | Niezdefiniowana - jeśli strona ma ten typ, prawdopodobnie jest wolna |
| pag_header | 1 | Strona nagłówka bazy danych |
| pag_pages | 2 | Strona inwentarza stron (lub strona inwentarza przestrzeni - SIP) |
| pag_transactions | 3 | Strona inwentarza transakcji (TIP) |
| pag_pointer | 4 | Strona wskaźnikowa |
| pag_data | 5 | Strona danych |
| pag_root | 6 | Strona główna indeksu |
| pag_index | 7 | Strona indeksu (drzewa B) |
| pag_blob | 8 | Strona danych blob |
| pag_ids | 9 | Gen-ids |
| pag_log | 10 | Informacje o dzienniku zapisu z wyprzedzeniem |
Każda strona ma swój nagłówek zawierający informacje o typie strony i numerze następnej strony tego samego typu. Pełną listę parametrów zawartych w nagłówku każdej strony możemy uzyskać, rozważając strukturę pag w pliku definicji ods.h.
/\* Podstawowy nagłówek strony */
typedef struct pag {
SCHAR pag_type; /*identyfikator typu strony*/
SCHAR pag_flags; /*flagi strony*/
USHORT pag_checksum; /*suma kontrolna strony: równa 12345 po wersji 5.0 */
ULONG pag_generation; /*generacja strony */
ULONG pag_seqno; /* numer sekwencyjny WAL ostatniej aktualizacji - przestarzałe*/
ULONG pag_offset; /* przesunięcie WAL ostatniej aktualizacji - przestarzałe*/
} *PAG;
Typy stron i ich zastosowanie
Rozważmy szczegółowo każdy typ strony i poznajmy ich funkcję oraz zawarte w nich informacje. Zaczniemy krok po kroku - od pierwszej strony.
Każda operacja na bazie danych zaczyna się od odczytu strony nagłówka bazy danych (lub strony nagłówkowej). Strona nagłówka bazy danych znajduje się jako pierwsza we wszystkich plikach bazy danych. W związku z tym jest przedstawiona jako pierwsza na obrazku 2 (jeśli wyobrazimy sobie, że obrazek przedstawia rozciągnięcie pliku bazy danych od lewej do prawej, z góry na dół).
Strona nagłówkowa zawiera informacje o bazie danych jako całości. Na obrazku 3 strona danych jest przedstawiona w sposób, w jaki pokazuje nam ją IBSurgeon:

Obrazek 3. Strona nagłówka bazy danych.
Możesz uzyskać wyobrażenie o zawartości strony nagłówkowej, otrzymując statystyki bazy danych. W tym celu możesz użyć narzędzia wiersza poleceń gstat lub innego wygodniejszego narzędzia do administracji InterBase z listy w dodatku „Narzędzia administratora i programisty InterBase”. Więcej szczegółów na temat procesu otrzymywania statystyk i opisu danych strony nagłówkowej znajdziesz w rozdziale „Statystyki”.
Należy zauważyć, że strona nagłówkowa zawiera tak ważne informacje, jak rozmiar strony, numer wersji ODS (informacje o tym znajdziesz poniżej), datę utworzenia bazy danych, informacje o transakcjach oraz zestaw różnych informacji. Na przykład Implementation ID przechowuje informacje o tym, pod jakim systemem operacyjnym została utworzona ta baza danych.
Podczas łączenia się z bazą danych serwer InterBase odczytuje pierwsze 1024 bajty informacji z początku pliku i na podstawie odczytanych wartości określa, czy plik wskazany w linii połączenia jest bazą danych InterBase, czy nie. Następnie serwer odczytuje numer wersji ODS ze strony nagłówkowej oraz rozmiar strony w tej bazie danych i, jeśli wersja ODS jest zgodna z implementacją serwera, ponownie odczytuje całą stronę nagłówkową, używając odpowiedniego rozmiaru strony otrzymanego z pierwszych 1024 bajtów. Po tym z strony nagłówkowej odczytywane są pozostałe ważne parametry bazy danych, takie jak tryb odczytu-zapisu, dialekt bazy danych itp.
Na stronie nagłówkowej znajduje się odniesienie do pierwszej ze stron wskaźnikowych, przechowujących odniesienia do stron danych zawierających metadane: tabela RDB$Pages (patrz poniżej w rozdziale „Logiczna struktura bazy danych InterBase”). Na obrazku 2 to odniesienie jest zilustrowane strzałką z napisem „Numer 1. strony wskaźnikowej w bazie danych”. Serwer odczytuje numer 1. strony wskaźnikowej ze strony nagłówkowej i przechodzi do niej. Strona wskaźnikowa składa się z uporządkowanej tablicy numerów stron danych, które tworzą określoną tabelę (tabela jest rozważana jako obiekt SQL, opisany przez logiczną strukturę bazy danych). Teraz możesz zobaczyć, jak IBSurgeon interpretuje stronę wskaźnikową (spójrz na obrazek 4):

Obrazek 4. Strona wskaźnikowa bazy danych InterBase
Strona zawiera wektor stron danych; te dane tworzą określoną tabelę w bazie danych. Ten wektor reprezentuje tablicę wskaźników odpowiadających numerom stron danych w pliku. Serwer odczytuje 4-bajtowy numer strony danych i przechodzi do odpowiedniej strony danych. Gdy przechodzi do 1. strony danych RDB$Pages, serwer zaczyna konstruować wewnętrzną reprezentację bazy danych, która jest później używana przez serwer do wszystkich operacji na bazie danych. RDB$Pages przechowuje odniesienia nie tylko do stron danych zawierających informacje o bazie danych, ale także do pozostałych stron, które odgrywają rolę w zapewnieniu działania bazy danych.
Często wspominamy o tej tabeli, która ściśle rzecz biorąc należy do logicznej struktury bazy danych. Niemniej jednak wszystko jest ze sobą powiązane, dlatego nie możemy opisać czegoś, nie odwołując się do czegoś innego.
Jednym z ważnych typów stron jest strona inwentarza transakcji (TIP). Strony te, podobnie jak wszystkie strony, składają się z nagłówka i części głównej, która reprezentuje tablicę sekwencji 2-bajtowych. Sekwencje te opisują stan transakcji w bazie danych (więcej szczegółów na temat transakcji znajduje się w rozdziale „Transakcje”).
Tabela 4. Możliwe stany transakcji w TIP
| Wartość sekwencji na PIP | Znaczenie |
| 0 | Transakcja nie rozpoczęła się, jest aktywna lub utracona bez zatwierdzenia lub wycofania |
| 1 | Transakcja wykonała Commit |
| 2 | Transakcja wykonała Rollback |
| 3 | Transakcja limbo (dla 2PC) |
Każda wersja rekordu ma swój identyfikator transakcji, co pozwala jednocześnie wykonywanym transakcjom „dowiedzieć się” o stanie innych i rozwiązywać konflikty podczas pracy wieloużytkownikowej (więcej o wersjach rekordów i innych kwestiach w rozdziale „Wielogeneracyjna architektura InterBase”).
Strona nagłówka bazy danych, strony wskaźnikowe i TIP należą do typów stron „porządkowych” (housekeeping), używanych wyłącznie przez serwer. Użytkownicy InterBase nigdy jawnie nie uzyskują informacji, które zawierają. Strony przechowujące informacje o alokacji stron (zwykle nazywane stronami inwentarza stron (PIP) lub stronami inwentarza przestrzeni (SIP)) również należą do typu stron porządkowych. Strony te znajdują się począwszy od drugiej, to znaczy pierwsza PIP znajduje się tuż po stronie nagłówkowej, i pojawiają się w bazie danych w stałych odstępach stron innych typów. Rozmiar tych odstępów oznacza, co ile stron innych typów pojawia się PIP, i zależy od rozmiaru strony ustawionego dla tej bazy danych. Strony inwentarza stron nie są uwzględniane na stronach wskaźnikowych i nie są wskazywane w RDB$Pages. Integralność tych stron jest kluczowa dla pomyślnej pracy całej bazy, ponieważ zawartość PIP opisuje stan wszystkich pozostałych stron w bazie danych. Każda strona bazy danych może mieć 3 stany: nieprzydzielona, przydzielona z wolną przestrzenią, przydzielona i pełna. Gdy istnieje potrzeba dodatkowej przestrzeni dla nowych danych, serwer sprawdza PIP w celu znalezienia stron nieprzydzielonych. Jeśli taka strona istnieje, serwer zmienia jej stan na przydzielona z wolną przestrzenią. Jeśli nie ma stron nieprzydzielonych, baza danych się rozszerza - dodawana jest nowa strona danych.
Przykład strony danych w IBSurgeon i danych, które zawiera, pokazano na obrazku 5.

Obrazek 5. Strona inwentarza stron
Gdy tylko strona zostanie przydzielona, InterBase zapisuje jej stan na SIP, a następnie zapisuje samą stronę. Po tym musimy dodać tę nowo utworzoną stronę do jakiejś dużej liczby stron, na przykład do stron danych dla tabeli. W tym celu powinniśmy zapisać odwołanie do tej nowej strony na ostatniej stronie tej dużej liczby stron - na przykład na ostatniej stronie danych tabeli. Jeśli serwer przerwał swoją pracę tuż po zapisaniu na SIP, ale nie zapisał odwołania na stronach, które odnoszą się do właśnie przydzielonej strony, wtedy ta strona staje się osierocona. Strona osierocona jest fizycznie utworzona, zarezerwowana na SIP, ale nie ma do niej odwołań z innych stron, co oznacza, że serwer nie będzie w stanie jej znaleźć i zapisać na niej danych. Strona osierocona jest oznaczona czerwonym kwadratem na obrazku 2. Strony osierocone powstają głównie w wyniku nieoczekiwanego wyłączenia zasilania serwera i są „leczone” przez specjalne narzędzie do naprawy baz danych gfix (lub przez FirstAID) (lub przez IBSurFirstAID).
Przed rozważeniem stron danych powinniśmy wspomnieć o ważnych typach stron: stronach generatorów i stronach indeksów. Strony generatorów reprezentują tablicę liczb 4-bajtowych, pokazujących stany generatorów. W rzeczywistości generator to zwykły licznik.
Na obrazku 6 możesz zobaczyć stronę generatora. Zwróć uwagę, że chociaż IBSurgeon pokazuje nazwy generatorów, nie oznacza to, że te nazwy są przechowywane na stronach generatorów. Zrobiono to dla wygody użytkownika badającego bazę danych. W rzeczywistości nazwy generatorów są przechowywane w tabeli systemowej RDB$Generators.

Obrazek 6. Strona generatora (g en-ids)
Jak widać w tym przykładzie, baza danych zawiera generatory systemowe, zaczynające się od prefiksu RDB$, oraz generatory zdefiniowane przez użytkownika. Jeśli chcesz dowiedzieć się o funkcji i zastosowaniu generatorów podczas tworzenia aplikacji baz danych InterBase, zobacz rozdział „Tabele. Klucze podstawowe i generatory”. Strony generatorów są uwzględniane wraz z innymi stronami w tabeli RDB$Pages.
Każda tabela ma co najmniej jedną stronę główną indeksów, niezależnie od tego, czy ma indeksy, czy nie. Strona ta zawiera wskaźniki do stron indeksów dla danej tabeli. Można powiedzieć, że strona główna indeksów ma takie samo znaczenie dla stron indeksów, jak strona wskaźnikowa dla stron danych. Dlatego IBSurgeon przedstawia ją w podobny sposób. Przykład strony głównej indeksów pokazano na obrazku 7.

Obrazek 7. Strona główna indeksów
Strona główna indeksów zawiera listę stron, na których przechowywane są wartości indeksów, a także informacje o indeksie - selektywność indeksu i różne flagi. Więcej szczegółów na temat indeksów, ich roli i zastosowania w bazach danych InterBase znajdziesz w rozdziale „Indeksy”.
Strony indeksów zawierają bezpośrednio wartości indeksów lub, jeśli poziom indeksu > 0, odwołania do leżących poniżej stron indeksów. Oto przykład strony indeksu (obrazek 8).

Obrazek 8. Strona indeksu (B-drzewa)
Strona indeksu przechowuje spakowane wartości indeksowanych danych. Stosowany jest dość skomplikowany mechanizm indeksowania, zwłaszcza przy tworzeniu indeksów złożonych (obejmujących kilka pól).
Ogólnie rzecz biorąc, strony danych i strony zawierające wartości BLOB przechowują informacje użytkownika. Strony danych zawierają rekordy w tabelach użytkownika bazy danych, fragmenty rekordów, stare wersje, różnice między wersjami, pola BLOB i tak dalej. Jeśli chodzi o pola BLOB, są one powiązane z rekordami na stronach danych i zawierają dane o dużym rozmiarze, które nie mogą być umieszczone na stronie danych. Referencyjny sposób przechowywania wartości BLOB pozwala na przechowywanie dużych danych.
Przykład prezentacji strony danych w IBSurgeon pokazano na obrazku 9:

Obrazek 9. Strona danych
Nagłówek strony danych zawiera typ strony, identyfikator tabeli właściciela (relationID). Rekordy są przechowywane na stronach danych od końca strony i są przydzielane bliżej początku strony w miarę ich wypełniania.
Możemy się o tym przekonać, patrząc na indeksy wierszy, zawierające 2 wartości - przesunięcie na stronie i jej długość. Jak widać na początku wiersza znajdują się rekordy przydzielone na końcu strony - na przykład pierwszy rekord ma przesunięcie 8156 bajtów i długość 34 bajtów - dlatego kończy się na 8156+34=8192 bajtach - na samym skraju strony (w naszym przypadku rozmiar strony wynosi 8192 bajty). Gdy strona jest wypełniona (danymi od góry i indeksami wierszy od dołu), serwer zaczyna zapisywać nowe rekordy i wersje starych rekordów na nowych stronach. Z opisanego powyżej mechanizmu wypełniania stron łatwo wywnioskować, dlaczego specjaliści InterBase zdecydowanie zalecają używanie stron danych o dużym rozmiarze (minimum 4096 bajtów, lepiej 8192). Jeśli utworzymy tabelę, której jeden rekord będzie dość duży (na przykład 10 pól VARCHAR (255)), zajmie on, po wypełnieniu, ponad 2550 bajtów. Oznacza to, że taki rekord będzie zbyt duży dla strony o małym rozmiarze (1024 lub 2048). Oczywiste jest, że konieczność wczytania kilku stron z dysku w celu odczytania jednego rekordu nie przyspieszy pracy z bazą danych. Dlatego zaleca się zmianę rozmiaru strony danych podczas tworzenia lub przywracania bazy danych, ponieważ domyślnie ustawiony jest rozmiar 1024 bajtów. Właśnie krótko omówiliśmy główne typy stron plików danych InterBase i ich funkcje. Teraz możemy przejść na wyższy poziom strukturalny.
ODS
ODS to skrót od On-Disk Structure, czyli struktury danych bazy danych InterBase na dysku. ODS definiuje, jak dane w plikach bazy danych są zorganizowane. Definicja głównych stałych i struktur danych do implementacji struktury na dysku znajduje się w pliku ods.h z zestawu kodów źródłowych InterBase. ODS zmieniał się w trakcie rozwoju InterBase, a podczas pracy z konkretną bazą danych serwer sprawdza numer wersji ODS, aby wiedzieć, z czym ma do czynienia. Plik ods.h przedstawia nam następujące wersje struktury na dysku:
-
ODS 5 był używany przez InterBase 3.3 i nie jest obsługiwany przez nowsze wersje
-
ODS 6 i ODS 7 nigdy nie zostały wydane
-
ODS 8 jest używany przez InterBase 4.0
-
ODS 9 jest używany przez InterBase 4.5 i nowsze
-
ODS 10 pojawił się z InterBase 6
-
ODS 11 pojawił się z InterBase 7.0
Oprócz głównych wersji ODS istnieją wersje pomocnicze, które zależą od konkretnej wersji serwera bazy danych, który je utworzył. Główne numery wersji są zapisane w części całkowitej liczby, wskazującej wersję, a pomocnicze - w części ułamkowej. Na przykład wersja serwera 4.0 tworzy bazy danych z ODS 8.0, a InterBase 4.2 - 8.2. Przejście między wersjami pomocniczymi w górę jest wykonywane automatycznie. Na przykład wystarczy otworzyć bazę z ODS 8.0, utworzoną przez serwer 4.0, w InterBase 5.6, a ODS tej bazy będzie miał wersję 8.2. Przejście między głównymi wersjami bazy danych jest wykonywane tylko przez kopię zapasową bazy przy użyciu starej wersji i przywrócenie przy użyciu nowej wersji serwera. Proces przejścia między wersjami jest szczegółowo opisany w rozdziale 1.4 „Migracja”.
Ważnym momentem w implementacji obsługi ODS dla wersji InterBase 4.x i 5.x jest wsteczna kompatybilność serwerów InterBase 4.x i 5.x z wersją o jeden niższą niż implementacja konkretnego serwera. InterBase obsługuje kilka możliwych ODS i zgodnie z jej wersją ODS podczas łączenia się z konkretną bazą danych wybiera obsługę wymaganej implementacji ODS. Mechanizm podejmowania decyzji o tym, którą implementację obsługi ODS wybrać w konkretnym przypadku, nazywa się Y-Valve ((c) Steve Trenton).
Mówiąc prościej, baza danych z ODS 8.x, odpowiadająca InterBase 4.0, może być otwarta w InterBase 5.x.
Pełna tabela kompatybilności ODS jest pokazana poniżej:
| Wersja InterBase | Główny ODS | Pomocniczy ODS |
| 4.0/4.1 | 8.0 | … |
| 4.2 | 8.2 | 8.2 |
| 5.0/5.1 | 9.0 | 8.2 |
| 5.5 | 9.1 | 8.2 |
| 5.6 | 9.1 | 8.2 |
| 6.0 | 10.0 | 9.0/9.1 |
| 7.0 | 11.0 | 10.0 |
| 7.1 | 11.1 | 10.0 |
ODS ma kompatybilność wsteczną. Innymi słowy, serwer z wyższą wersją i wszystkie jego narzędzia będą w stanie pracować z bazą danych utworzoną przez wcześniejsze wersje serwera, ale nie odwrotnie. Jeśli spróbujesz otworzyć bazę danych utworzoną w wersji InterBase 6, używając InterBase 5.x, otrzymasz komunikat o błędzie „Unsupported On-disk structure: Found ODS 10, supported ODS 9”.
Opis przejścia między wersjami w górę i odwrotnie znajduje się w rozdziale „Migracja”.
ODS jest bardzo ważny dla kwestii związanych z kopiami zapasowymi i ekstrakcją bazy danych, a także przywracaniem uszkodzonych baz danych. Narzędzia do tworzenia kopii zapasowych gbak i przywracania gfix monitorują wersję ODS i po prostu nie będą działać, jeśli wersja ODS bazy danych, której mają służyć, jest większa niż wersja w nich zaimplementowana. Oznacza to, że gbak z 4.x nie będzie w stanie utworzyć kopii zapasowej bazy danych, jeśli została utworzona przez serwer 5.x, chociaż odwrotnie jest łatwe.
Most między fizyczną i logiczną strukturą bazy danych
Rozważyliśmy fizyczną strukturę plików bazy danych w ogólny sposób. Teraz musimy przejść do logicznej struktury bazy danych. Zbudujmy most między fizycznym i logicznym poziomem reprezentacji informacji w bazie danych, aby nie było rozgraniczenia w pojęciach i luk w materiale. Wszystko, co jest przechowywane na różnych stronach bazy danych, musi być w jakiś sposób zorganizowane w pamięci komputera; dane z pliku bazy danych muszą zostać przekształcone w zestaw wewnętrznych obiektów i zmiennych serwera. Ten zestaw nazywa się wewnętrznym obrazem bazy danych zgodnie z terminologią Ann Harrison [1.. Spróbujemy więc rozważyć proces tworzenia wewnętrznego obrazu bazy danych.
-
Сервер читає 1024 байти з початку файлу, і якщо це дійсно файл бази даних InterBase, він визначає розмір сторінки цієї бази та повторно зчитує всю сторінку заголовка.
-
Зі сторінки заголовка сервер отримує номер сторінки покажчика, яка зберігає посилання на сторінки даних, визначаючи таблицю RDB$Pages.
-
Сервер переходить на цю сторінку покажчика та починає читати інформацію зі сторінок даних, на які вона вказує. Він заповнює першу таблицю RDB$Pages даними. Ця таблиця є своєрідним мостом між фізичними об’єктами - сторінками файлів бази даних - та логічними - таблицями. Структура RDB$Pages, як і інших системних таблиць, строго фіксована в InterBase.
-
Отримавши дані про розподіл сторінок за відношеннями (відношення - насправді це те саме, що й звичайні таблиці, і ми можемо подумки замінювати ці поняття для спрощення), InterBase починає формувати структури даних: спочатку системні таблиці, обмеження та індекси, а потім і користувацькі об’єкти.
-
Після ініціалізації системних і користувацьких метаданих (таблиць, обмежень, індексів та інших об’єктів бази даних) InterBase повертає дескриптор цієї бази даних користувачу, який попросив відкрити базу. По суті, дескриптор - це ідентифікатор, який показує InterBase, з якою базою даних працювати, оскільки кілька користувачів можуть працювати одночасно, а це означає, що може бути відкрито кілька баз даних.
-
Після цих операцій база даних вважається відкритою, і сервер готовий виконувати користувацькі запити до неї. Тепер, коли створено певний міст, що з’єднує фізичну та логічну структуру бази даних, ми можемо почати вивчати особливості логічної структури.
Логічна структура бази даних InterBase
Логічна структура - це досить розмите поняття, тому ми спробуємо поступово засвоїти ключові ідеї, сподіваючись, що згодом вони стануть інтуїтивно зрозумілими. Перше, що ми розглянемо стосовно логічної структури бази даних, - це системні таблиці та їхній вміст. Системні таблиці описують систему, а також користувацькі метадані. Взагалі кажучи, термін «метадані» означає «дані, що описують набір даних». Префікс «мета» означає: «описує набір». Наприклад, метамова - це мова, що описує набір мов. Метадані описують користувацькі дані, тобто таблиці, тригери, представлення, збережені процедури тощо - усе, що реалізує правила зберігання та обробки інформації, заради якої створено цю конкретну базу даних.
Досить цікаво вперше дізнатися, що всі метадані - користувацькі таблиці, тригери, представлення, а також усі системні об’єкти - зберігаються в тих самих таблицях, з яких можна читати та записувати дані звичайними SQL-запитами. Ці таблиці «візуально» відрізняються лише тим, що їхні назви починаються з RDB$. Ці 4 символи зарезервовані для назв системних об’єктів. Жодна користувацька таблиця, стовпець чи інший об’єкт не має права мати назви, що починаються з цих символів. Формально ви можете створити таблицю, назва якої починається із зарезервованих символів, але документація InterBase не рекомендує цього робити.
Виникає питання: якщо дані про структуру бази даних зберігаються в тих самих таблицях, що й користувацькі дані, то де зберігається інформація про таблиці, що описують таблиці? Класичний приклад проблеми «курки та яйця» - як одне могло з’явитися раніше за інше, якщо вони взаємозалежні? Відповідь полягає в тому, що системні таблиці в їхньому примітивному стані зафіксовані в початкових кодах InterBase і автоматично відкриваються під час створення бази даних у певному порядку. Ми вже говорили про таблицю RDB$Pages, яка зіставляє фізичні сторінки у файлах бази даних із певними об’єктами цієї бази. Структуру цієї таблиці наведено нижче:
Таблиця 5. Системна таблиця RDB$Pages
| Назва стовпця | Тип даних | Опис |
| RDB$PAGE_NUMBER | INTEGER | Номер фізичної сторінки |
| RDB$RELATION_ID | SMALLINT | Ідентифікатор таблиці, для якої виділено сторінку |
| RDB$PAGE_SEQUENCE | INTEGER | Номер цієї сторінки |
| RDB$PAGE_TYPE | SMALLINT | Тип сторінки - див. таблицю 3 |
Кожна сторінка даних пов’язана з певною таблицею. Цей зв’язок підтримується полем RDB$RELATION_ID, де зберігається посилання на таблицю. Як описано вище, у процесі побудови внутрішнього образу бази даних сервер створює цю таблицю та заповнює її даними за певним алгоритмом. Якщо бути точним, на момент побудови внутрішнього образу бази даних RDB$Pages - це не таблиця, а просто файл даних певного формату, відомого InterBase. За фіксованим алгоритмом сервер читає дані з цього файлу та створює таблицю - RDB$Relations - яка є важливою для всієї бази даних. Ця таблиця описує всі таблиці бази даних. Якщо виконати SQL-запит:
SELECT * from RDB$Relations
щоб дізнатися, посилання на які таблиці містить RDB$Relations, ми побачимо, що вона містить RDB$Pages і саму себе. Очевидно, що в цьому випадку сервер трохи хитрує, підставляючи ці та інші системні таблиці в RDB$Relations заднім числом, таким чином легітимізуючи їх. Він реєструє їх як «звичайні» таблиці, у які може додавати або видаляти записи. Іншими словами, надає стандартний SQL-інтерфейс для роботи з метаданими.
І може виникнути цілком розумне питання - навіщо розробникам InterBase пристосовувати свої системні дані до користувацького інтерфейсу? Адже внутрішні механізми доступу та операції читання були б швидшими. Звісно, у наданні універсального механізму роботи з таблицями, що описують метадані, є великий сенс.
Річ у тім, що логічна структура бази даних складається не лише з таблиць, а й з інших об’єктів. В InterBase існують такі об’єкти:
-
Таблиця
-
Представлення
-
Тригер
-
Обчислюване поле
-
Перевірка (валідація)
-
Процедура
-
Індекс виразу
-
Виняток
-
Користувач
-
Поле
-
Індекс
-
Функція, визначена користувачем (UDF)
Поки що ми точно не знаємо функцій деяких об’єктів, але точно знаємо, що всі вони мають бути описані та збережені у вигляді, зручному для користувача та для доступу з ядра InterBase. Найкраще було б зберігати ці об’єкти в системних таблицях. Їхнє додавання та модифікація виконуються за допомогою SQL-запитів. Розумне рішення, чи не так? Реалізація сервера повністю відокремлена від конкретної бази даних - усі взаємозв’язки описані за допомогою SQL та його розширень - мови збережених процедур і тригерів.
Отже, усі об’єкти сервера зберігаються в таблицях. Для кожного типу об’єктів існує таблиця, що описує всі екземпляри, описані в базі даних. Наприклад, для тригерів існує таблиця RDB$Triggers, для збережених процедур - RDB$Procedures, представлення описані в таблиці RDB$Relations.
Розглянемо детально структуру останньої таблиці, що описує всі таблиці та представлення в базі даних. Структуру таблиці RDB$RELATIONS взято з Language Reference для InterBase 6 і наведено нижче в таблиці 6.
Таблиця 6. Системна таблиця RDB$Relations
| Назва стовпця | Тип даних | Довжина | Опис |
| RDB$VIEW_BLR | BLOB | 80 | BLR: для представлень містить BLR (Binary Language Representation) запиту, який InterBase виконує щоразу під час звернення до представлення. |
| RDB$VIEW_SOURCE | BLOB | 80 | Текст: для представлень містить код SQL-запиту, що реалізує це представлення. |
| RDB$_DESCRIPTION | BLOB | 80 | Користувацький опис таблиці або представлення |
| RDB$RELATION_ID | SMALLINT | Містить внутрішній ідентифікатор таблиці/представлення | |
| RDB$SYSTEM_FLAG | SMALLINT | Визначає тип таблиці: користувацькі дані - 0; системна інформація > 0. | |
| RDB$DBKEY_LENGTH | SMALLINT | Довжина db$key | |
| RDB$FORMAT | SMALLINT | Зарезервовано для внутрішнього використання InterBase. Містить лічильник модифікацій метаданих для даної таблиці. | |
| RDB$FIELD_ID | SMALLINT | Кількість полів у таблиці. | |
| RDB$RELATION_NAME | CHAR | 31 | Унікальна назва таблиці. |
В описі цієї системної таблиці ми бачимо абревіатуру BLR. Щоб зрозуміти, що це таке, зробимо екскурс у SQL. Як відомо, представлення, тригери та збережені процедури - це код, написаний розширенням мови SQL (для кожного сервера СКБД існують власні розширення). Він близький до людської мови, що дозволяє легко складати запити. Але InterBase, очевидно, перекладає його на щось більш «машинне» - а саме в BLR (Binary Language Representation). Будь-який запит, представлення, тригер, збережена процедура завжди перекладаються в BLR, а потім передаються в ядро InterBase для виконання.
BLR
BLR - це спеціальна мова, яка використовується як проміжна ланка між SQL-кодом, який пише програміст, і машинним кодом, який приймає сервер. Ніхто не пише безпосередньо в BLR - це було б досить складно, оскільки для максимально можливої швидкості виконання в цій мові використовується так званий зворотний польський запис. Ось невеликий приклад:
blr_begin,
blr\_assignment,
blr\_field, 0, 7, 'D','A','T','E','I','Z','M',
blr\_variable, 1,0,
blr\_assignment,
blr\_field, 0, 4, 'R','A','T','E',
blr\_variable, 0,0,
blr\_block,
BLR для ваших запитів, процедур, тригерів та інших тригерів формується спеціальним препроцесором, який є частиною ядра сервера. Як показано в таблиці 7, для представлень зберігається як їхній текст (початковий), так і скомпільоване представлення, тобто BLR. Під час звернення до будь-якого об’єкта, що має BLR, сервер виконує двійковий код об’єкта і не інтерпретує щоразу початковий текст цих об’єктів, що дозволяє пришвидшити виконання складних запитів.
Ієрархія об’єктів в InterBase
Щоб мати чітке уявлення про те, що являють собою об’єкти бази даних, спробуємо побудувати ієрархію об’єктів бази даних за принципом «хто що містить». Фізичні сторінки файлів бази даних - це перше, що має бути включене в нашу ієрархію як найнижчий рівень організації даних. Потім ідуть таблиці як основні об’єкти, що описують усі інші типи об’єктів. Таблиці описують збережені процедури, тригери, обчислювані поля, перевірки, індекси виразів, винятки тощо. Зверніть увагу - лише описують! Таблиці містять лише декларації та визначення цих об’єктів, а самі об’єкти реалізуються через BLR. Тому ми можемо представити таблиці у вигляді каркаса, що підтримує всі інші об’єкти бази даних. BLR буде внизу каркаса як шар реалізації, потім тригери, збережені процедури, індекси виразів і представлення.
Щоб заспокоїти фахівців із внутрішньої структури InterBase, які можуть заперечити, що BLR багатьох об’єктів (таких як представлення) зберігаються в системних таблицях, зауважимо, що це ставлення досить складно відобразити на малюнку, і для спрощення ми ним нехтуємо. Схема не має на меті абсолютно точно відтворити взаємозалежності об’єктів бази даних; вона лише ілюструє їхній тісний взаємозв’язок.
Той факт, що ці типи об’єктів безпосередньо пов’язані з BLR, який їх реалізує без будь-якої проміжної логіки, їх об’єднує. Винятки слід виділити окремо - вони являють собою особливі типи помилок, визначених користувачем. Винятки обробляються на рівні ядра InterBase і тому не мають BLR. Такі типи обмежень, як перевірки, розміщені над тригерами, оскільки насправді тригери реалізують логіку обмежень і перевірок.
Ієрархію об’єктів логічної та фізичної структури бази даних зображено на малюнку 2.
Малюнок 10. Об’єкти логічної структури бази даних InterBase
Звісно, ця схема описує логічну структуру та взаємозв’язки об’єктів у базі даних лише приблизно та дає загальне уявлення про неї. Кожен, хто хоче вивчити структуру метаданих бази даних InterBase, може виконати реінжиніринг системних таблиць бази даних і розглянути всі взаємозв’язки між її об’єктами, а також звернутися до документації та початкових кодів InterBase. Ця таблиця показує лише основні об’єкти бази даних. Опишемо коротко основні функції, які ці об’єкти виконують у базі даних.
Tabele - główny obiekt, zawierający dane użytkownika i systemowe. Tabela ma unikalną nazwę i zawiera zestaw nazwanych pól. Użytkownik może umieszczać dane w tabelach, wyodrębniać je i modyfikować. Można powiedzieć, że tabela jest podobna do zwykłych papierowych tabel rysowanych ręcznie.
Wyzwalacze - wykonywalne fragmenty kodu, używane do implementacji dodatkowych akcji w czasie operacji na danych. Wyzwalacze są wykonywane przed lub po operacjach wstawiania, modyfikacji lub usuwania i pozwalają na realizację podstawiania wartości do nowo tworzonych rekordów oraz wielu innych rzeczy.
Procedura składowana to potężne narzędzie do implementacji logiki biznesowej na poziomie bazy danych. Wykonywana na poziomie serwera działa bardzo szybko i pozwala na wykonanie zestawu operacji na zbiorach danych. Procedury składowane InterBase zwracają standardowe zbiory danych SQL, na których można wykonywać wszystkie operacje SQL, w tym łączenie z innymi tabelami.
Widoki to skompilowane zapytania SQL, wykonywane na serwerze. Widoki pozwalają organizować zbiory danych, przenosząc część logiki biznesowej na serwer.
Walidacje to ograniczenia nałożone na wartości pól w tabeli. Na przykład możemy wskazać, że dane pole będzie przyjmować tylko wartości dodatnie. Ograniczenia na wartości pól są implementowane przez wyzwalacze i pozwalają skutecznie kontrolować integralność referencyjną na poziomie bazy danych. Zazwyczaj ograniczenia są używane, aby zapobiec umieszczaniu w tabeli błędnych wartości.
Użytkownicy - InterBase pozwala nam mieć kilku użytkowników do pracy z bazą danych i rozdzielić między nich prawa dostępu do różnych obiektów bazy danych. W ten sposób możemy kontrolować uprawnienia do tych lub innych operacji na bazie danych.
Funkcje zdefiniowane przez użytkownika (UDF) - funkcje zdefiniowane przez użytkownika. To jedna z najpotężniejszych możliwości InterBase, pozwalająca nam rozszerzyć standardowy interfejs SQL o własne funkcje. Na przykład funkcje pracy z ciągami znaków, takie jak UPPER (ustawianie wszystkich symboli w górnym rejestrze), są zaimplementowane w standardowej bibliotece UDF, dołączonej do zestawu InterBase. Dzięki możliwości tworzenia własnych UDF, programiści mogą rozszerzyć funkcjonalność InterBase praktycznie o dowolne funkcje. Do tworzenia UDF możemy użyć dowolnego środowiska programistycznego, które pozwala na tworzenie bibliotek dynamicznych (Visual C++, C++ Builder, Delphi itp.).
Podsumowanie
W tym rozdziale po raz pierwszy rozważyliśmy kwestie dotyczące implementacji przechowywania i przetwarzania danych w bazie danych InterBase. Niestety, nie możemy dokonać krótkiego przeglądu tego tematu bez odwoływania się do dużej liczby terminów i niedokładnych analogii. Gdybyśmy opisali fizyczną i logiczną strukturę bazy danych bardziej szczegółowo, musielibyśmy i tak odnieść się do podstawowych kodów InterBase, ale to byłaby już inna książka.
Niemniej jednak uważamy, że każdemu programiście przydałoby się zapoznanie z zawartością produktu, którego używa na co dzień.
Bibliografia
-
«The On-Disk Structure of InterBase» autorstwa Ann.W.Harrison
-
«Space Management in InterBase» autorstwa Ann W.Harrison
-
«Structure of a Data Page» autorstwa Paula Beacha (Z podziękowaniami dla Dave’a Schneppera i Deeja Bredenberga)