W. Bartkiewicz Projektowanie i programowanie systemów informatycznych Wykład 6. Indeksowanie plików – Indeksy drzewiaste ISAM i drzewa B+ Drzewa ISAM – Organizacja sekwencyjno-indeksowa • W organizacji indeksowo-sekwencyjnej ISAM (Indexed-Sequential Access Method) drzewo indeksowe ma charakter statyczny. Tworzone jest ono dla pewnego istniejącego już zestawu stron podstawowych i nie jest później modyfikowane. • Operacje wstawiania nowych wpisów do indeksu oraz ich usuwania wykonywane są tylko liściach, czyli stronach podstawowych. Strony na wyższych poziomach drzewa podczas tych operacji nie ulegają zmianie. • Założenie o statycznym charakterze drzewa indeksowego powoduje problem z przepełnianiem się stron przy dodawaniu rekordów. Nie można również dodawać nowych stron liści, znajdujących się w porządku logicznym, „pomiędzy” istniejącymi stronami, ponieważ wymagałoby to modyfikacji węzłów również na wyższych poziomach. Drzewa ISAM – Dodawanie nowych kluczy 31 Dodajemy klucze: 28, 48, 43, 49, 45 15 5 8 15 38 22 17 19 22 25 30 31 34 36 38 50 42 47 50 53 54 58 Drzewa ISAM – Dodawanie nowych kluczy 31 15 5 8 15 38 22 17 19 22 25 30 31 28 Strony nadmiarowe 34 36 38 50 42 47 50 48 43 49 45 53 54 58 Drzewa ISAM – Dodawanie nowych kluczy • W organizacji indeksowo-sekwencyjnej w przypadku braku miejsca na stronie, dodawane są do niej nowe strony nazywane stronami przepełnienia (nadmiarowymi). • Nie są one indeksowane lecz dołączane w postaci sekwencyjnej listy stron. • Strony przepełnienia mogą łączyć się w dłuższe łańcuchy. Drzewa ISAM – Usuwanie kluczy • Usuwanie wpisów z drzewa ISAM jest w zasadzie odwróceniem operacji wstawiania. Rekord indeksu zawierający klucz jest po prostu usuwany ze strony podstawowej lub nadmiarowej na której się znajduje. • Strony indeksu na wyższych poziomach drzewa pozostają nie zmienione, nawet jeśli na stronie podstawowej indeksu usunięty został klucz o najwyższej wartości. • Drzewo ISAM ma charakter statyczny, a więc usunięcie ze strony podstawowej nawet wszystkich rekordów nie powoduje zmian na wyższych jego poziomach. Oznacza to jednocześnie, że pusta strona podstawowa nie może być usunięta. • W przypadku stron nadmiarowych usunięcie wszystkich rekordów może powodować usunięcie również samej strony. Drzewa ISAM – Usuwanie kluczy 31 Usuwamy klucze: 28, 38 15 5 8 15 38 22 17 19 22 25 30 31 28 Strony nadmiarowe 34 36 38 50 42 47 50 48 43 49 45 53 54 58 Drzewa ISAM – Usuwanie kluczy 31 15 5 8 15 38 22 17 19 22 25 30 31 34 36 50 42 47 50 48 43 49 Strony nadmiarowe 45 53 54 58 Drzewa ISAM – Struktura pliku indeksowego • Ponieważ w organizacji indeksowo-sekwencyjnej nie można wstawiać (ani usuwać) nowych stron podstawowych w kolejności logicznej pomiędzy stronami istniejącymi, możemy więc założyć, że są one również fizycznie uporządkowane zgodnie z tą kolejnością. • Przed zbudowaniem wyższych poziomów drzewa podstawowe dane indeksu są po prostu fizycznie sortowane zgodnie z porządkiem indeksowania. • Strony podstawowe umieszczane są na ogół na początku pliku indeksowego. Po niech występuje (również statyczny) zestaw stron na wyższych poziomach drzewa. Ponieważ strony przepełnienia dodawane są dynamicznie, wobec tego umieszczane są one na końcu pliku indeksowego. Drzewa ISAM – Wyszukiwanie • Wyszukiwanie w strukturze ISAM przebiega w sposób typowy dla przeszukiwania indeksu drzewiastego. • Pamiętać jednak należy, że jeśli strona liścia zawiera jakieś dowiązane strony przepełnienia, w celu znalezienia odpowiedniej wartości klucza może zachodzić potrzeba przeszukania również i tych dodatkowych stron. • Obniża to oczywiście efektywność wyszukiwania, zwiększając liczbę stron indeksu jakie muszą zostać wczytane z dysku. • Pamiętać należy, że dane na stronach nadmiarowych mogą być co prawda sortowane, jednak w większości przypadków z operacji tej się rezygnuje, aby zredukować czas dodawania nowych rekordów. Również i ta właściwość wpływa więc negatywnie na czas przeszukiwania stron w pamięci operacyjnej. Drzewa ISAM – Wyszukiwanie 31 Szukamy klucza: 45 15 5 8 15 38 22 17 19 22 25 30 31 34 36 50 42 47 50 48 43 49 Strony nadmiarowe 45 53 54 58 Drzewa ISAM – Wyszukiwanie zakresowe • Przy wyszukiwaniu zakresowym w drzewie ISAM znajdowana strona na której występuje jest najniższa wartość klucza z wyspecyfikowanego zakresu. • Następnie aż do znalezienia górnego krańca przedziału, wyszukiwane są kolejne strony podstawowe (liście indeksu), z uwzględnieniem ewentualnych stron przepełnienia. • Jak już wspomnieliśmy w organizacji indeksowo-sekwencyjnej zakłada się że są one w pliku indeksowym ułożone po kolei, zgodnie z porządkiem indeksowania. Drzewa ISAM – Efektywność wyszukiwania • Efektywność wyszukiwania w drzewie ISAM jest nadal bardzo wysoka, tym niemniej może ona wyraźnie spadać w sytuacji gdy w indeksie występują długie łańcuchy stron nadmiarowych. • Jeśli już dojdzie do ich powstania jedynym sposobem ich usunięcia (oczywiście nie biorąc pod uwagę operacji usuwania danych) jest kompletne przeorganizowanie pliku indeksowego, tzn. po prostu posortowanie rekordów indeksu, a następnie zbudowanie od początku nowego drzewa ISAM. • Aby uniknąć konieczności reindeksacji danych w organizacji indeksowo-sekwencyjnej, podczas tworzenia indeksu zazwyczaj strony podstawowe wypełniane są jedynie w około 80%. Przy w miarę jednorodnym rozkładzie danych łańcuchy stron przepełnienia będą wówczas występowały rzadko i ich długość będzie niewielka. Drzewa ISAM – Specyfika przy wielodostępie • Drzewa ISAM mają jeszcze jedną dodatkową zaletę, o której należy również wspomnieć. • W warunkach współbieżnego dostępu do danych, podczas modyfikacji zmieniane strony muszą zostać na czas ich przebudowy zablokowane dla wszystkich pozostałych użytkowników. • Ponieważ organizacji indeksowo-sekwencyjnej przy dodawaniu i usuwaniu rekordów w modyfikowane są wyłącznie strony podstawowe (liście), strony samego drzewa indeksowego nie muszą być blokowane. • Właściwość ta może stanowić istotną przewagę organizacji ISAM nad dynamicznymi strukturami w rodzaju drzew B+, o których będziemy mówili w następnym punkcie bieżącego rozdziału. Drzewa B+ – Charakterystyka • Drzewa B+ są przykładem dynamicznej struktury indeksowej. Nowe strony mogą być wstawiane w dowolnym miejscu indeksu, bez konieczności tworzenia łańcuchów stron nadmiarowych. • Wiąże się to z koniecznością dynamicznego rozszerzania i kurczenia samego drzewa. • Proste implementacje operacji wstawiania i usuwania węzłów drzewa mogą prowadzić do powstawania drzew niezrównoważonych, tzn. takich w których pewne gałęzie są dużo dłuższe od pozostałych. • Z punktu widzenie wyszukiwania danych w pamięci zewnętrznej byłaby to cecha wysoce niepożądana, ponieważ dla przeszukania tych gałęzi niezbędne byłoby załadowanie do pamięci operacyjnej wielu stron indeksu. • Algorytmy modyfikacji drzew B+, które scharakteryzujemy krótko w dalszej części wykładu, zapewniają zrównoważenie powstającego drzewa. Drzewa B+ – Charakterystyka • Powodem nadmiernego rozrostu struktury drzewa może być również zbyt niski stopień wykorzystania węzłów drzewa. Kontrola wolnego miejsca na stronach pamięci dynamicznego drzewa indeksowego jest nieco trudniejsza niż w przypadku organizacji indeksowo sekwencyjnej, która tworzona jest raz, a potem nie ulega modyfikacji. • Tak więc większość stron w drzewie ISAM będzie wypełnionych w całości. Procedury wstawiania i usuwania węzłów w drzewie B+ zapewniają, że wykorzystana będzie co najmniej połowa miejsca na stronie każdego węzła, z wyjątkiem korzenia. • Zakłada się również, że wszystkie węzły mają jednakową liczbę miejsc na potencjalne wpisy na stronie, jest to ponadto wielkość parzysta. Oznaczmy ją przez 2k. Tak więc liczba wpisów na stronie n dla węzłów drzew B+ spełnia warunek k ≤ n ≤ 2k (ponieważ jak wspomnieliśmy wyżej co najmniej połowa miejsca na stronie jest wykorzystana). Parametr k nazywany jest zwykle rzędem drzewa. Drzewa B+ – Wyszukiwanie • Wyszukiwanie rekordu odbywa się dokładnie według procedury przechodzenia po drzewie, omówionej wcześniej. • Ponieważ nie ma stron nadmiarowych, do znalezienia każdej z wartości klucza odczytane muszą zostać jedynie strony na kolejnych poziomach drzewa. • Ponieważ stopień wykorzystania stron węzłów drzewa B+ może być niższy niż w przypadku drzewa ISAM, liczba poziomów drzewa (nazywana również wysokością drzewa) może być nieco większa. Nadal jednak przypadki gdy wysokość drzewa będzie większa niż 3 lub 4 będą bardzo rzadkie. Drzewa B+ – Wyszukiwanie • W drzewach B+ pojawia się problem przeszukiwania zakresowego. • Ponieważ drzewo ma charakter dynamiczny, w czasie jego istnienia strony pamięci zarówno węzłów na wyższych poziomach jak i liści mogą być wstawiane i usuwane, nie możemy więc założyć fizycznego uporządkowania stron zgodnie z kluczem indeksowania (tak jak to było w przypadku organizacji ISAM). • Dlatego, aby można było znaleźć kolejne występujące po sobie rekordy z zakresu wyszukiwania, strony podstawowe indeksu (liście) muszą być uformowane w dwukierunkową listę stron. Każda z nich przechowuje odniesienie do strony „następnej” i „poprzedniej” w porządku sortowania. Drzewa B+ – Dodawanie wpisu Dodajemy klucz: 36 22 18 19 22 25 31 35 39 39 48 44 48 66 52 66 Brak miejsca na stronie 70 73 77 Drzewa B+ – Dodawanie wpisu Dodajemy klucz: 36 22 18 19 22 25 31 39 35 36 39 Kopiujemy na wyższy poziom 48 66 44 48 Brak miejsca na stronie 52 66 70 73 77 Drzewa B+ – Dodawanie wpisu Dodajemy klucz: 36 Przenosimy na wyższy poziom 22 18 19 22 31 25 31 39 48 35 36 39 44 48 66 52 66 70 73 77 Drzewa B+ – Dodawanie wpisu Dodajemy klucz: 36 39 22 18 19 22 31 25 31 48 35 36 39 44 48 66 52 66 70 73 77 Drzewa B+ – Algorytm dodawania klucza 1. 2. 3. Stosujemy algorytm wyszukiwania w drzewie dla znalezienia liścia w którym powinien znaleźć się nowy wpis. Przyjmijmy, że znaleziony liść to strona indeksu podstawowego K. Jeśli na stronie K jest jeszcze miejsce, tzn. n < 2k, dodajemy nowy wpis i kończymy algorytm. W przypadku przeciwnym przechodzimy do punktu 2. Dzielimy liść na dwie strony, powiedzmy K1 i K2, pozostawiając wpisy (rekordy) od 1 do k na pierwszej z nich i przenosząc rekordy od k+1 do 2k na drugą. Wstawiamy nowy wpis na odpowiednią stronę. Musimy pamiętać o modyfikacji drzewa na wyższym poziomie. Niech L będzie węzłem rodzicielskim dla K, tzn. stroną zawierającą wpis dla największej wartości klucza na stronie K i odpowiadające mu odniesienie do tej strony. Musimy dodać do węzła L informacje o nowo dodanym liściu. Tworzymy nowy wpis, kopiując największą wartość klucza na stronie K1 (czyli wpis k na którym dokonano podziału). Drzewa B+ – Algorytm dodawania klucza 4. Jeśli w węźle L jest miejsce na dodanie nowego wpisu, robimy to i kończymy algorytm. Jeśli nie dzielimy stronę L na dwie, kopiując połowę rekordów, dodajemy w odpowiednim miejscu nowy wpis. Modyfikujemy drzewo na wyższym poziomie, przenosząc (a nie kopiując) największą wartość klucza na nowej stronie do węzła rodzicielskiego, powtarzamy więc krok 4. Jeśli podzielona strona była korzeniem drzewa, dodajemy nowy poziom i tworzymy nowy korzeń. Drzewa B+ – Dodawanie klucza • • • • Po dodaniu nowego wpisu do indeksu, w naszym wcześniejszym przykładzie otrzymaliśmy w wyniku tej operacji drzewo trzypoziomowe. Do poprawnego przeszukiwania indeksu prezentowanego w tym przykładzie, tak naprawdę wystarczą w zupełności dwa poziomy drzewa B+. W przypadku nierównomiernego rozkładu danych dodawanych do indeksu algorytm wstawiania nowych kluczy mógłby powodować nadmierne rozrastanie któregoś z poddrzew. Algorytm ten w swej najprostszej postaci jest więc poprawny, ale może tworzyć drzewa niezrównoważone. Drzewa B+ – Dodawanie wpisu 39 22 18 19 22 31 25 31 48 35 36 39 44 48 66 52 66 70 73 77 Drzewa B+ – Dodawanie klucza – Redystrybucja • • • • • Aby uniknąć tworzenia drzew niezrównoważonych, czasami stosuje się wariant omówionego wyżej algorytmu wstawiania z wykorzystaniem tzw. redystrybucji kluczy indeksowania. Redystrybucja dotyczy tzw. węzłów sąsiednich. Poprzez sąsiadów w drzewie rozumiemy węzły znajdujące się bezpośrednio obok siebie i mające wspólnego rodzica. Redystrybucja stosowana jest w przypadku gdy strona do której dodajemy wpis jest pełna. Generalnie rzecz biorąc polega ona sprawdzeniu czy jest jakieś wolne miejsce na stronach węzłów sąsiedzkich. Jeśli tak, to zamiast dzielić pełny węzeł na dwa, przenosimy z niego jeden rekord do sąsiada, modyfikując odpowiednio węzeł rodzicielski. Jeśli miejsca nie ma, to stronę dzielimy i postępujemy dokładnie jak w algorytmie omówionym wyżej. Drzewa B+ – Dodawanie klucza – redystrybucja Dodajemy klucz: 36 22 18 19 22 25 31 35 39 39 48 44 48 66 52 66 70 73 77 Drzewa B+ – Dodawanie klucza – Redystrybucja Dodajemy klucz: 36 22 18 19 22 25 31 35 36 39 39 48 44 48 66 52 66 70 73 77 Drzewa B+ – Dodawanie klucza – Redystrybucja Dodajemy klucz: 36 25 18 19 22 25 31 35 36 39 39 48 44 48 66 52 66 70 73 77 Drzewa B+ – Dodawanie klucza – Redystrybucja • • • Redystrybucja pozwala często utworzyć drzewo o lepszej strukturze. Jednak trzeba pamiętać, że metoda ta wiąże się ze zwiększeniem kosztów obliczeniowych aktualizacji drzewa, bez żadnej gwarancji poprawy działania. Jeśli po sprawdzeniu w obu sąsiednich węzłach drzewa okaże się że nie ma miejsca na przesuniecie wpisów, to i tak musimy stronę podzielić. Koszty ładowania do pamięci operacyjnej dodatkowych stron okażą się wtedy niepotrzebnym wydatkiem, zwłaszcza w przypadku węzłów na wyższych poziomach drzewa. Z tego powodu redystrybucja ograniczana jest zwykle do liści drzewa. Podział strony liścia wymaga modyfikacji listy stron i wstawienia do niej nowej strony. Tak więc w tym przypadku redystrybucja nie wymaga żadnych dodatkowych kosztów, ponieważ strony sąsiednie muszą być i tak załadowane w celu zmiany powiązań organizujących listę. Drzewa B+ – Usuwanie wpisów Usuwamy klucz: 22 39 22 18 19 22 31 25 31 48 35 36 39 44 48 66 52 66 70 73 77 Drzewa B+ – Usuwanie wpisów Może pozostać – nie modyfikujemy Usuwamy klucz: 22 39 22 18 19 31 25 31 48 35 36 39 44 48 66 52 66 70 73 77 Drzewa B+ – Usuwanie wpisów – redystrybucja Usuwamy klucz: 52 39 22 18 19 31 25 31 48 35 36 39 44 48 66 52 66 70 73 77 Drzewa B+ – Usuwanie wpisów – redystrybucja 39 22 18 19 31 25 31 48 35 36 39 44 48 Usuwamy klucz: 52 Mniej niż połowa wpisów na stronie – próbujemy redystrybucji 66 66 70 73 77 Drzewa B+ – Usuwanie wpisów – redystrybucja Usuwamy klucz: 52 Musimy zmodyfikować 39 22 18 19 31 25 31 48 35 36 39 44 48 66 66 70 73 77 Drzewa B+ – Usuwanie wpisów – redystrybucja Usuwamy klucz: 52 39 22 18 19 31 25 31 48 35 36 39 44 48 70 66 70 73 77 Drzewa B+ – Usuwanie wpisów – likwidacja stron Usuwamy klucz: 48 39 22 18 19 31 25 31 48 35 36 39 44 48 70 66 70 73 77 Drzewa B+ – Usuwanie wpisów – likwidacja stron 39 22 18 19 31 25 31 48 35 36 39 44 Usuwamy klucz: 48 Mniej niż połowa wpisów na stronie, redystrybucja niemożliwa 70 66 70 73 77 Drzewa B+ – Usuwanie wpisów – likwidacja stron Usuwamy klucz: 48 39 Usuwamy 22 18 19 31 25 31 48 35 36 39 44 70 66 70 Scalamy dwie strony sąsiednie 73 77 Drzewa B+ – Usuwanie wpisów – likwidacja stron Usuwamy klucz: 48 Mniej niż połowa wpisów na stronie 39 22 18 19 31 25 31 70 35 36 39 44 66 70 73 77 Drzewa B+ – Usuwanie wpisów – likwidacja stron 39 22 18 19 31 25 31 Przenosimy w dół wpis i usuwamy Usuwamy klucz: stronę 48 Scalamy dwie strony sąsiednie 70 35 36 39 44 66 70 73 77 Drzewa B+ – Usuwanie wpisów – likwidacja stron Usuwamy klucz: 48 22 18 19 25 31 31 35 36 39 39 70 44 66 70 73 77 Drzewa B+ – Algorytm usuwania wpisu 1. 2. Stosujemy algorytm wyszukiwania w drzewie dla znalezienia liścia w którym znajduje się usuwany wpis. Przyjmijmy, że znaleziony liść to strona indeksu podstawowego K. Jeśli strona K wypełniona jest w ponad połowie, tzn. n > k, usuwamy wpis i kończymy algorytm. W przypadku przeciwnym przechodzimy do punktu 2. Ponieważ na stronie jest mniej niż połowa wpisów musimy skorygować zawartość liścia, wykorzystując liść sąsiedni. Oznaczmy znalezionego sąsiada przez M. Jeśli zawiera on więcej niż połowę wpisów (n > k) dokonujemy redystrybucji, przesuwając wpis na liść K i odnotowując tę sytuację w węźle rodzicielskim. Kończymy algorytm usuwania. Jeśli natomiast M ma dokładnie połowę wpisów (n = k), przechodzimy do punktu 3. Drzewa B+ – Algorytm usuwania wpisu 3. 4. Jeśli w liściu M znajduje się dokładnie połowa wpisów nie możemy wykonać redystrybucji, ponieważ z kolei M byłby wtedy wypełniony w zbyt niskim stopniu. Mamy więc do czynienia z sytuacją w której suma wpisów z obydwu liści mieści się na jednej stronie. Możemy więc scalić je w jeden węzeł. Przenosimy wszystkie wpisy na jedną stronę, a następnie niepotrzebny, pusty liść (powiedzmy M) usuwamy z listy stron podstawowych indeksu. Przechodzimy do punktu 4. Po usunięciu węzła drzewa musimy zmodyfikować rodzica, którego oznaczymy przez L: usuwamy powiązanie do skasowanej strony, a dla strony pozostawionej modyfikujemy odpowiednio klucz kierujący do niej przeszukiwanie. Jeśli w węźle L pozostała co najmniej połowa wpisów, kończymy algorytm. W przeciwnym przypadku przechodzimy do punktu 5. Drzewa B+ – Algorytm usuwania wpisu 5. Ponownie staramy się uzupełnić wpisy w węźle L, korzystając z sąsiada. Oznaczmy przez W węzeł sąsiedni dla L. Jeśli wpisy z węzłów W i L nie mieszczą się na jednej stronie lub wypełniłyby stronę w całości nie możemy scalić W i L. Dokonujemy wówczas redystrybucji wpisu z W do L, zaznaczając nowe wartości kluczy kierujących na te strony w ich węźle rodzicielskim. Kończymy algorytm. W przypadku przeciwnym przechodzimy do punktu 6. Drzewa B+ – Algorytm usuwania wpisu 6. Jeśli suma wpisów w węzłach W i L nie wypełnia całej strony, tzn. po połączeniu pozostanie na niej co najmniej jedno miejsce, możemy te węzły scalić. Przenosimy wszystkie wpisy na jedną stronę (powiedzmy W), drugą zwalniając. Dodatkowe miejsce potrzebne będzie nam na przesunięcie klucza rozdzielającego W i L w węźle rodzicielskim (występującego między odniesieniami do W i do L) do pozostawionego węzła W. Postępujemy więc dokładnie odwrotnie jak w przypadku podziału strony nie będącej liściem drzewa. Następnie musimy zmodyfikować węzeł rodzicielski, powtarzamy więc czynności od punktu 4 do 6. Jeśli algorytm nie zakończy się w sposób określony w punkcie 4 lub 5 kontynuujemy go do chwili osiągnięcia korzenia drzewa. Jeśli węzeł korzenia zostanie opróżniony całkowicie z wpisów usuwamy go, zmniejszając liczbę poziomów drzewa o 1. Drzewa B+ – Usuwanie wpisów – redystrybucja na wyższych poziomach drzewaNie można scalić – za dużo wpisów 42 22 18 19 31 25 31 39 70 35 36 39 40 41 42 44 66 70 73 77 Drzewa B+ – Usuwanie wpisów – redystrybucja na wyższych poziomach drzewa 39 22 18 19 31 25 31 42 35 36 39 40 41 42 70 44 66 70 73 77