Indeksy Statyczny model pamiętania bazy danych Bazy danych są fizycznie przechowywane jako pliki rekordów, które zazwyczaj są składowane na twardych dyskach. Dane przechowywane są w pamięci zewnętrznej, która jest logicznie podzielona na segmenty (lub obszary), które z kolei składają się z jednostek o stałej długości, zwanych stronami (ang. pages). Spójny obszar pamięci zewnętrznej, w którym zapamiętana może być jedna strona nosi nazwę bloku (ang. block, slot). Pożądane jest, aby wszystkie bloki (a zatem i strony) były jednakowej wielkości. Ponadto strona nie musi być na stałe przypisana do jednego bloku, lecz w różnych chwilach czasowych może być zapamiętywana w różnych blokach. Każdy plik F(R) rekordów typu R pamiętany jest w jednym segmencie. W pamięci operacyjnej wydzielony jest obszar zwany buforem zdolnym do pomieszczenia jednej strony. Przesyłanie danych pomiędzy pamięciami operacyjną i zewnętrzna przedstawia następujący schemat: Schematyczne ujęcie organizacji pamięci i wymiany danych systemu pamiętania. Celowość wprowadzenia segmentów Zbiór wszystkich stron wygodnie jest łączyć w jednostki logiczne wyższego rzędu zwane segmentami (ang. segment) lub obszarami (ang. area). Fakty, które uzasadniają celowość wprowadzenia pojęcia segmentu, związane są z funkcjami, które powinien realizować SZBD: 1. Bezpośredni (on-line) lub pośredni (off-line) dostęp do danych. Baza może być tak duża, że jest rzeczą niemożliwą, aby wszystkie zawarte w niej były dostępne bezpośrednio. Konieczne więc staje się wydzielenie takich jej fragmentów, które znajdują się w pamięci o dostępie bezpośrednim i takich, które znajdują się w pamięci o dostępie pośrednim. 2. Blokowanie dostępu do danych. W przypadku współbieżnego korzystania z bazy danych przez wielu użytkowników powstaje konieczność zapewnienia wyłączności dostępu do pewnych jej fragmentów dla jednego z użytkowników. Z punktu widzenia wydajności systemu byłoby niedopuszczalne traktowanie całej bazy danych jako tego fragmentu. Konieczny jest zatem podział bazy danych na mniejsze jednostki. 3. Odtwarzanie bazy danych. W celu odtworzenia bazy danych w przypadku awarii (sprzętowej lub programowej) należy periodycznie tworzyć jej kopie. Ponieważ zmiany w bazie danych nie dotyczą z jednakową częstotliwością wszystkich zapamiętanych w niej danych, celowy jest podział jej na mniejsze części. Dzięki temu protokółowanie zmian w bazie danych odnosić się może tylko do pewnych wybranych jej fragmentów, co pozwala zmniejszyć koszt prowadzenia protokołów. 4. Zapewnienie poufności danych. Ze względu na problem poufności danych system musi zapewnić mechanizm sprawdzania upoważnień dostępu (autoryzację) dla poszczególnych użytkowników. Jednakże ani poziom, ani rodzaj poufności danych nie jest jednakowy w całej bazie danych. Celowy jest zatem podział bazy danych na mniejsze fragmenty. 1
Odwzorowanie rekordów na stronie Przykład. Wystąpienie rekordu typu PRACOWNIK( identyfikator, nazwisko, zawód, płaca, wiek ) można zapisać na stronie w następujący sposób: a) każda wartość atrybutu zapisywana jest w polu o stałej długości (podanej w ilości znaków), Długość pola w znakach 3 15 15 5 2 123 Kowalski muzyk 34543 39 b) wartości wszystkich atrybutów zapisywane są w kolejnych polach o najmniejszym rozmiarze potrzebnym do ich zapamiętania i odseparowane wyróżnionym znakiem, np. % 123 % Kowalski % muzyk % 34543 % 39 % c) wartości wszystkich atrybutów zapisywane są w kolejnych polach o najmniejszym rozmiarze potrzebnym do ich zapamiętania i utworzenie dla każdego rekordu części przedrostkowej składającej się ze wskaźników podających adresy końców poszczególnych pól Adresowanie rekordów w bazie danych Na pliku F(R):={ r 1, r 2,..., r N } rekordów typu R mogą być wykonywane następujące operacje: wprowadzanie rekordu, usuwanie rekordu, modyfikacja rekordu, poszukiwanie rekordu o określonej wartości atrybutu. Dostęp do miejsca zapamiętania w pamięci wystąpienia rekordu można uzyskać np. przy pomocy identyfikatora, którym może być np. kolejny numer przypisany rozważanemu rekordowi przez SZBD ( w Oracle jest to ROWID ). Definicja indeksu Indeks to struktura danych umożliwiająca szybszy dostęp do konkretnych rekordów w pliku, a tym samym przyśpieszenie realizacji zapytań. Indeksem pliku F(R) nazywamy zbiór I:={ (x,a) : r F(R) (x = key(r) a = kbd(r) ) }, gdzie key(r) jest wartością np. klucza głównego w wystąpieniu rekordu r, a kbd(r) kluczem bazy danych (adresem) wystąpienia rekordu r. Każdą parę (x,a) I nazywamy elementem indeksu. Klucz bazy danych kbd każdego rekordu pozostaje niezmienny w ciągu całego czasu, w którym dany rekord istnieje w bazie danych. kbd jest pewną symboliczną wartością, na podstawie której może być określony fizyczny adres rekordu i składa się on z 2 części: identyfikatora typu rekordu, którego wystąpieniem jest rozważany rekord, kolejnego numeru przypisanego danemu rekordowi przez SZBD w obrębie zbioru wszystkich rekordów tego samego typu. 2
Indeks w bazie danych: spełnia podobną rolę jak skorowidz (indeks) w książce, jest strukturą pomocniczą, stowarzyszoną z plikiem danych i wykorzystywaną do wyszukiwania konkretnych informacji, zapobiega konieczności sekwencyjnego przeszukiwania całego pliku przy każdej próbie odczytu wybranego elementu w przypadku relacyjnej bazy danych rekordu lub grupy rekordów, jest posortowany, a każda jego pozycja zawiera jedną z wartości, które mogą być wyszukiwane oraz jeden lub więcej adresów (numery rekordów), pod którymi można znaleźć poszukiwaną wartość, zazwyczaj odpowiada ustalony klucz wyszukiwania. Struktura indeksu Zawartość struktury indeksu stanowią rekordy składające się z wartości klucza i adresu w pliku danych, pod którym umieszczony jest logiczny rekord zawierający tę wartość klucza. Plik danych to plik zawierający rekordy logiczne. Plik indeksu to plik zawierający rekordy indeksu. Wartości w pliku indeksu są posortowane według pola indeksującego, które zwykle odpowiada pojedynczemu atrybutowi. Rodzaje indeksów W praktyce indeksy mogą być realizowane na różne sposoby. Wśród głównych typów indeksów wyróżniamy: indeks główny plik danych jest uporządkowany według pola porządkującego, pole indeksujące jest równe polu porządkującemu, jego wartości w poszczególnych rekordach są unikalne. indeks pomocniczy jest to indeks zdefiniowany na podstawie pola, które nie jest wykorzystywane przy wyznaczaniu porządku rekordów w pliku danych. Indeks pomocniczy można utworzyć na polu, które jest kluczem kandydującym i posiada unikatową wartość w każdym rekordzie lub które nie jest polem klucza i posiada powtarzające się wartości. Plikowi danych może odpowiadać co najwyżej jeden indeks główny oraz dowolna liczba indeksów pomocniczych. Indeks główny i indeks pomocniczy Indeks główny to plik uporządkowany z rekordami o stałej długości, które składają się z dwóch pól. Pierwsze z tych pól ma taki sam typ danych jak pole klucza uporządkowania w pliku danych, zwanego kluczem głównym, zaś drugie z tych pól jest wskaźnikiem na blok dyskowy (adres bloku). Indeks pomocniczy, analogicznie jak indeks główny, jest plikiem danych posortowanym według określonego klucza. To, co istotnie różni te dwa typy indeksów, to fakt, iż plik danych, któremu odpowiada indeks główny, jest posortowany także według klucza indeksującego, co nie jest konieczne w przypadku indeksu pomocniczego. Różnice pomiędzy indeksem głównym i indeksem pomocniczym: 3
plik danych, któremu odpowiada indeks główny, jest posortowany także według klucza indeksującego, co nie jest konieczne w przypadku indeksu pomocniczego, wartości klucza w indeksie pomocniczym nie muszą być unikalne, co ma miejsce w indeksie głównym. indeks pomocniczy nie wyznacza pozycji rekordu w pliku z danymi. Plik indeksu w przypadku indeksu głównego wymaga znacznie mniejszej liczby bloków niż plików danych, co jest konsekwencją dwóch przyczyn: w pliku indeksu w przypadku indeksu głównego występuje mniej wpisów niż w pliku danych. każdy wpis indeksu ma zazwyczaj mniejszy rozmiar niż rekord danych, ponieważ posiada tylko dwa pola. A zatem w jednym bloku może pomieścić się więcej wpisów indeksu niż rekordów danych. Dlatego wyszukiwanie wykonywane na pliku indeksu wymaga uzyskania dostępu do mniejszej liczby bloków niż ma to miejsce przy przeszukiwaniu pliku danych. Indeksy pomocnicze umożliwiają przyśpieszenie wykonywania zapytań, w których kryteria dotyczą innych atrybutów niż atrybut (atrybuty) indeksu głównego. Ceną, jaką za to płacimy, towarzyszy dodatkowy koszt związany z uaktualnieniem indeksów przy modyfikacji danych. Przykład indeksu głównego Niektóre bloki uporządkowanego (sekwencyjnego) pliku rekordów PRACOWNICY z polem NazwImie jako polem lucza uporządkowania Na każdy blok w pliku danych przypada jeden wpis indeksu (ang. index entry), inaczej rekord indeksu (ang. index record) w pliku indeksu. Każdy wpis indeksu posiada wartość pola klucza głównego dla pierwszego rekordu w bloku oraz wskaźnik na ten blok, co na rysunku poniżej oznaczyliśmy <K(i), KBD(i)>. W celu utworzenia indeksu głównego na pliku uporządkowanym przedstawionym dwa slajdy wcześniej, zostało użyte jako klucz główny pole NazwImie, ponieważ jest to pole klucza uporządkowania pliku (zakładamy, iż każda wartość w polu NazwImie jest unikatowa). Każdy wpis w indeksie zawiera wartość pola NazwImie oraz wskaźnik. 4
Pierwsze trzy wpisy indeksu dla rozważanego pliku mają postać: <K(1) = (Abarowski Michał), KBD(1) = adres bloku 1.> <K(2) = (Adamczyk Monika), KBD(1) = adres bloku 2.> <K(3) = (Aliński Robert), KBD(1) = adres bloku 3.> Indeks główny na polu klucza uporządkowania pliku rekordów PRACOWNICY Jak można wywnioskować choćby na podstawie powyższego przykładu, całkowita liczba wpisów w indeksie jest taka sama jak liczba bloków dyskowych w uporządkowanym pliku danych. Pierwszy rekord w każdym bloku pliku danych nosi nazwę rekordu zaczepienia takiego bloku lub w skrócie zaczepienia bloku. Sposoby implementacji indeksów pomocniczych Indeksy pomocnicze można implementować wykorzystując w tym celu następujące podejścia: utworzenie indeksu gęstego, który zawiera osobną pozycję dla każdego rekordu w pliku danych oznacza to możliwość wielokrotnych wystąpień w indeksie tej samej wartości, w indeksie pomocniczym jest tworzona jedna pozycja dla każdej wartości klucza występującej w pliku danych. Ponadto jest dopuszczalna możliwość, iż pojedyncza pozycja indeksu zawiera wiele wskaźników rekordów w pliku danych ( o tej samej wartości klucza). w indeksie pomocniczym jest tworzona jedna pozycja dla każdej wartości klucza występującej w pliku danych. Tym niemniej wskaźnik odpowiadający danej wartości klucza nie dotyczy pliku danych, lecz zawiera odsyłacz do pojemnika zawierającego wskaźniki odpowiednich rekordów w pliku danych. 5
Przykład. Na poniższym rysunku przedstawiono typowy indeks pomocniczy. W pliku danych umieszczono po dwa rekordy w jednym bloku, przy czym w rekordach zostały podane tylko pola z kluczem wyszukiwania (jak łatwo zauważyć ten atrybut ma wartość całkowitą, która jest wielokrotnością 10). Nietrudno zauważyć, iż dane nie zostały posortowane według klucza wyszukiwania, w przeciwieństwie do kluczy w pliku indeksowym, które zostały posortowane. Dlatego też wskaźniki z tego samego bloku indeksowego mogą prowadzić do różnych bloków danych, i to nie tylko do jednego lub kilku sąsiednich. Przykładowo, aby odszukać wszystkie rekordy z kluczem 20, nie tylko koniecznym jest sprawdzenie dwóch bloków indeksów, ale także koniecznym jest sięgnięcie do trzech różnych bloków danych. Stąd też stosowanie indeksu pomocniczego może powodować konieczność wykonania znacznie większej liczby dostępów do dysku niż przy wyszukiwaniu tej samej liczby rekordów za pomocą indeksu głównego. Rodzaje indeksów Indeksy możemy podzielić także w następujący sposób: indeksy rzadkie w tego typu indeksie tylko niektóre wartości klucza wyszukiwania występujące w pliku mają swoje odpowiedniki w indeksie. Indeks główny jest indeksem niezagęszczonym (rzadkim), gdyż zawiera wpis dla każdego bloku dyskowego pliku danych oraz klucza jego rekordu zaczepienia, a nie dla każdej wartości wyszukiwania (czyli dla każdego rekordu). indeksy gęste tego typu indeks zawiera wszystkie wartości klucza wyszukiwania występujące w pliku danych, a zatem zawiera wpis dla każdej wartości klucza wyszukiwania (a stąd dla każdego rekordu) znajdującej się w pliku danych. Indeks pomocniczy jest zawsze gęsty (i na ogół zawiera powtórzenia). Indeksowane pliki sekwencyjne Indeksowanym plikiem sekwencyjnym nazywamy posortowany plik danych wraz z jego indeksem głównym. Indeksowany plik sekwencyjny umożliwia: przetwarzać rekordy sekwencyjnie, tj. w kolejności ich sortowania, uzyskanie bezpośredniego dostępu do wybranego rekordu za pomocą indeksu poprzez odpowiednią wartość klucza wyszukiwania. 6
Drzewo i jego własności Drzewa są taką strukturę danych, która w wielu SZBD jest wykorzystywana do przechowywania i organizacji indeksów. Drzewem nazywamy strukturę danych, która składa się z hierarchicznie zorganizowanego zbioru wierzchołków. Elementy składowe drzewa: każdy wierzchołek drzewa, za wyjątkiem korzenia, jest połączony z jednym wierzchołkiem oraz pewną liczbą wierzchołków nazwanych liśćmi (możliwe jest również, iż wierzchołek nie ma żadnego liścia), wierzchołek, który nie ma rodziców, nazywamy korzeniem, wierzchołek, który jest połączony krawędzią tylko z jednym wierzchołkiem nazywamy liściem. Głębokością (wysokością) drzewa nazywamy maksymalną liczbę poziomów pomiędzy korzeniem a liściem w drzewie. Jak wynika z tej definicji, głębokości odpowiadające różnym ścieżkom prowadzącym od korzenia do liści mogą być różne. W przypadku, gdy długość każdej ścieżki prowadzącej od korzenia do liścia jest taka sama, to takie drzewo nazywamy drzewem zbilansowanym lub B-drzewem. B-drzewa Niech h 0 i m 1. Drzewo (skierowane) T nazywamy B-drzewem klasy t(h,m), co zapisujemy T t(h, m), jeżeli T jest drzewem pustym (h = 0) lub spełnione są następujące warunki: wszystkie ścieżki prowadzące z korzenia drzewa do liści są jednakowej długości równej h, którą to liczbę nazywamy wysokością (głębokością) drzewa, każdy wierzchołek, z wyjątkiem korzenia i liści, jest początkiem co najmniej m + 1 krawędzi korzeń jest albo liściem, albo jest początkiem co najmniej dwóch krawędzi, korzeń jest liściem lub jest początkiem co najmniej dwóch krawędzi; z każdego wierzchołka wychodzi co najwyżej 2m + 1 krawędzi. B-drzewa są używane do tworzenia ścieżek dostępu dla wyszukiwania rekordów wg kluczy głównych. Rząd (stopień) drzewa Rzędem (stopniem) drzewa nazywamy maksymalną dopuszczalną liczbę krawędzi każdego wierzchołka. Im większy jest stopień (rząd) drzewa, tym bardziej drzewo jest (lub może być) szerokie i płytkie. Tym niemniej sam czas dostępu do elementów drzewa w większym stopniu zależy od jego głębokości niż od rzędu, dlatego korzystne jest tworzenie drzew płytkich i krzaczastych. Drzewami binarnymi nazywamy drzewa rzędu 2, tj. takie drzewa, w których każdy wierzchołek jest początkiem co najwyżej dwóch krawędzi. Przykład. Przykładowe B-drzewo klasy t(3,2) 7
Organizacja dostępu do pliku rekordów za pomocą B-drzewa Organizując indeks pliku F(R) w postaci B-drzewa należy każdemu wierzchołkowi przyporządkować jedną stronę danych w następujący sposób: p 0 x 1 a 1 p 1 x 2 a 2 p 2 x q p q a q gdzie: 1. 1 q 2m w przypadku korzenia i m q 2m dla wierzchołków pośrednich i liści; wartość q jest liczbą elementów indeksu na stronie, 2. x i wartością klucza głównego wystąpienia rekordu, a i kluczem bazy danych odpowiadającym wystąpieniu rekordu o wartości klucza głównego x i, 1 i q, 3. p i wskaźnikiem na wierzchołek będącym końcem krawędzi wychodzącej z tego wierzchołka, 0 i l, przy czym dla każdego wierzchołka muszą być spełnione następujące warunki: 1) x 1 < x 2 <... < x q, 2) Niech P(p i ) oznacza wierzchołek wskazywany przez wskaźnik p i, 0 i l, a X(p i ) niech będzie zbiorem wartości klucza głównego zawartym w poddrzewie, którego korzeniem jest P(p i ). Wtedy: x X(p 0 ), x x 1, x X(p i ), x i < x < x i+1, 1 i < q, x X(p q ), x q < x. Przykład B drzewa klasy t(3,2) Przykład. Przykład organizacji indeksu w postaci B-drzewa klasy t(3,2) można przedstawić graficznie w następujący sposób: 8
Dla uproszczenia w wierzchołkach drzewa umieszczono tylko wartości klucza głównego pomijając związane z nim adresy (wartości klucza bazy danych). Organizacja dostępu do pliku rekordów za pomocą B-drzewa Algorytm wyszukiwania w pliku indeksowym zorganizowanym w postaci B-drzewa jest oczywisty. Mamy indeks I zorganizowany w postaci B-drzewa i wartość klucza głównego x. Indeks I jest zapamiętany na stronach. Należy wyznaczyć wartość klucza bazy danych a, takiego że (x,a) I lub stwierdzenie, że x nie występuje w indeksie I. W przypadku, gdy wyszukiwanie zakończyło się niepowodzeniem (w indeksie I brak poszukiwanego elementu ) możliwe jest dołączenie elementu x do indeksu I. Ma być dołączona para (x,a). Dołączanie to może być bezkolizyjne lub może spowodować przepełnienie strony (tzn. na stronie s zapamiętanych jest już 2m elementów indeksu). W pierwszym przypadku element (x,a) dołączany jest w ten sposób, by zachować rosnące uporządkowanie wartości klucza głównego na stronie, natomiast w drugim przypadku zastosować należy metodę kompensacji lub podziału. Algorytm kompensacji Metodę tę stosujemy wtedy, gdy jedna ze stron sąsiadujących ze stroną s zawiera j elementów indeksu, j < 2m. Oznaczmy te stronę przez s 1. Elementy ze stron s i s 1 z uwzględnieniem dołączanego elementu (x,a) i elementu (x',a'), dla którego wskaźniki po obu stronach wskazują strony s i s 1, porządkujemy w ciąg 2m + j +2 elementów. Element środkowy" (x i,a i ), gdzie i := entier( (2m + j +2)/2 ) wstawiamy w miejsce elementu (x',a'). 1. Jeśli s 1 leży na lewo od strony s, to elementy o numerach 1,2,,i 1 wstawiamy na stronę s 1, a elementy o numerach i + 1, i + 2,, 2m + j +2 na stronę s. 2. Jeśli s 1 leży na prawo od strony s, to elementy o numerach 1,2,,i 1 wstawiamy na stronę s, a elementy o numerach i + 1, i + 2,, 2m + j +2 na stronę s 1. Przykład. Dany jest fragment B-drzewa Chcemy dołączyć element indeksu o wartości klucza głównego 27 (dla uproszczenia drugie składowe elementów indeksu będziemy stale pomijać). Po uporządkowaniu ciągu wartości klucza głównego otrzymamy ciąg 20, 22, 26, 27, 34, 40, 45, 50. Po zastosowaniu metody kompensacji otrzymamy następujący fragment B-drzewa: 9
Algorytm podziału Niech strona s zawiera 2m elementów i strony sąsiednie (obie) są zapełnione. Należy dołączyć element (x,a) do strony s. W tym celu elementy ze strony s z uwzględnieniem dołączanego elementu (x,a) porządkujemy rosnąco otrzymując ciąg 2m + 1 elementów. Elementy o numerach 1, 2,..., m umieszczamy na stronie s, a elementy o numerach m + 2, m + 3,... 2m + 1 na nowo utworzonej stronie s 1. Element (x m+1, a m+1 ) dołączamy do strony będącej wierzchołkiem krawędzi, której końcem jest wierzchołek odpowiadający stronie s w ten sposób, aby wskaźnik będący po lewej stronie elementu (x m+1,a m+1 ) wskazywał stronę s, a wskaźnik będący po prawej stronie od tego elementu stronę s1. Może się zdarzyć, że strona ta jest zapełniona (zawiera już 2m elementów). Wówczas należy dokonać jej podziału lub kompensacji. Uwaga. Może zaistnieć przypadek, że z podziałem dojdziemy aż do korzenia. Przy podziale korzenia istnieje potrzeba utworzenia dwóch nowych stron (wierzchołków) - jeden s 1 zgodnie z opisanym już algorytmem oraz strony, która będzie nowym korzeniem B-drzewa. Operacja ta spowoduje powiększenie wysokości B-drzewa. Uwaga. Należy podkreślić, że stosowanie operacji podziału jest jedyną metodą wzrostu wysokości B- drzewa. Starujemy bowiem zawsze od drzewa złożonego tylko z jednego wierzchołka (strony), który jest jednocześnie korzeniem i jedynym liściem B-drzewa. W wyniku dołączania nowych elementów dochodzi do zapełnienia tego wierzchołka i w konsekwencji do jego podziału, co prowadzi do zwiększenia wysokości drzewa od h = 1 do h = 2. Kolejne dołączenia i kolejne operacje podziału mogą dalej zwiększać jego wysokość. Wstawianie elementów pliku indeksowego Przykład. Na kolejnych slajdach pokazany jest przykład wstawiania elementów pliku indeksowego. 10
Chcemy dopisać element o wartości klucza 95. Wartość klucza 95 powinna być dopisana do prawego liścia. Brak miejsca na sąsiednich wierzchołkach. Musimy dokonać podziału prawego wierzchołka używając ciągu 90, 92, 94, 95, 98 i element środkowy 94 powinien być dopisany do korzenia, w którym brak miejsca. Sąsiednich wierzchołków brak. Musimy dokonać podziału korzenia używając ciągu 35, 45, 81, 89, 94 11
B * drzewa B* drzewa są drzewami, które spełniają poniższe warunki: każdy wierzchołek, który nie jest liściem, powinien mieć co najmniej dwoje dzieci, liczba dzieci każdego wierzchołka (za wyjątkiem korzenia i liści) w drzewie rzędu n powinna być mniejsza niż n/2 i nie większa niż n; jeśli n/2 nie jest liczbą całkowitą, wtedy wartość tę zaokrąglamy w górę, w każdym liściu drzewa rzędu n powinno znajdować się co najmniej (n 1)/2 i co najwyżej (n 1) wartości kluczy; jeśli (n 1)/2 nie jest liczbą całkowitą, wtedy wartość tę zaokrąglamy w górę, liczba kluczy znajdujących się w wierzchołku nie będącym liściem jest o 1 mniejsza od liczby dzieci tego wierzchołka, drzewo powinno być zbilansowane, co oznacza, iż wszystkie ścieżki prowadzące od korzenia do liści powinny mieć te samą długość, liście połączone są w listę uporządkowaną według wartości klucza. 12
Różnica pomiędzy B-drzewami a B*-drzewami polega na tym, iż B-drzewa są drzewami o orientacji wierzchołkowej (tj. w każdym wierzchołku umieszczone są zarówno wartości klucza, jak i związane z nimi adresy klucze bazy danych rekordów), natomiast B*-drzewa mają orientację liściastą (tj. w wierzchołkach nie będących liśćmi zawarte są jedynie wartości klucza, natomiast związane z nimi dane adresy (klucze bazy danych) rekordów lub nawet całe rekordy umieszczane są jedynie w liściach). Budowa B * drzewa Każde B*-drzewo charakteryzują następujące trzy parametry: h* wysokość B*-drzewa, tj. długość dróg z korzenia do liści, długości wszystkich dróg są jednakowe (h* 2), m* liczba określająca liczbę wartości klucza w korzeniu B*-drzewa i wierzchołkach nie będących liśćmi korzeń zawiera o 1 do 2m* wartości klucza, natomiast każdy wierzchołek pośredni zawiera od m* do 2m* wartości klucza. m liczba określająca liczbę elementów indeksu w liściach liść zawiera od m do 2m elementów indeksu. Każda strona będąca wierzchołkiem B*-drzewa różnym od liścia ma następującą budowę: p 0 x 1 p 1 x 2 p 2 x q p q gdzie 1 q 2m* dla korzenia i m* q 2m* dla wierzchołków pośrednich; q oznacza liczbę kluczy na stronie; x i wartość klucza, 1 i q, a p i wskaźnik na wierzchołek będący synem, 0 i q. Każda strona będąca liściem B*-drzewa ma następującą budowę: x 1 a 1 x 1 a 2 x j a j gdzie j oznacza liczbę elementów indeksu na stronie, m j 2m, x i wartość klucza rekordu, 1 i j, a a i klucz bazy danych rekordu (adres) o wartości klucza x i, 1 i j. Uwaga. Wielkości stron tworzących wierzchołki B*-drzewa, zarówno liści, jak i stron nie będących liśćmi, są takie same. B * drzewa - własności Każde B*-drzewo odznacza się następującymi własnościami: x 1 < x 2 <. < x q dla wierzchołków nie będących liśćmi, x 1 < x 2 <. < x j dla liści, Niech P(p i ) oznacza wierzchołek wskazywany przez wskaźnik p i (0 i q), a X(p i ) niech będzie zbiorem wartości klucza zawartych w poddrzewie o korzeniu P(p i ). Wówczas: x X(p 0 ), x x 1 max{ x : x X(p 0 ) } = x 1, x X(p i ), x i < x x i+1 max{ x : x X(p i ) } = x i+1, 1 i q, x X(p q ), x q < x. 13
Uwaga. Napis max{ x : x X(p i ) } = x i+1 oznacza, że największa wartość ze zbioru X(p i ) ma być równa wartości x i+1. Innym słowy, każda wartość klucza leżąca na prawo od dowolnego wskaźnika p musi wystąpić w poddrzewie wskazywanym przez p i to na pozycji największej, tj. jako skrajny prawy element w liściach tego poddrzewa. Przykład B * drzewa klasy t*(3,2,2) Indeks bitmapowy Indeks bitmapowy jest zbiorem map bitowych dla każdego indeksowanego atrybutu relacji. Przykład. Imię Adam Zenon Adam Jan Karol Piotr Zbigniew Jan Zbiór map bitowych dla atrybutu Imię. Adam Zenon Jan Karol Piotr Zbigniew 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 Zbiór map bitowy dla atrybutu Imię można zorganizować przy pomocy B*-drzewa. 14
Indeksy z odwróconym kluczem W pliku indeksowym o strukturze B*-drzewa wartości klucza pamiętane są w postaci otrzymanej z pierwotnej wartości klucza przez odwrócenie kolejności bitów. Tak zbudowany plik indeksowy nazywamy plikiem indeksowym z odwróconym kluczem (ang. reverse key index). Przykład. Przykład indeksu o strukturze B-drzewa i indeksu z odwróconym kluczem, w którym widać, że wartości bliskie zostały rozproszone do różnych stron. Rozproszenie bliskich wartości klucza ma duże znaczenie przy współbieżnych transakcjach operujących na rekordach o bliskich wartościach klucza. Indeksy na wielu atrybutach Do tej pory rozważaliśmy przypadek indeksów dla pojedynczych atrybutów. Indeksy można tworzyć na kombinacji atrybutów, np. rok, miesiąc. Wtedy porządek należy rozumieć następująco: para (2007,03) poprzedza parę (2009,01), para (2009,01) poprzedza parę (2009,03). Działanie tak zbudowanych indeksów jest analogiczne do omawianych wcześniej indeksów na pojedynczych atrybutach. 15