INDEKSY I SORTOWANIE ZEWNĘTRZNE Przygotował Lech Banachowski na podstawie: 1. Raghu Ramakrishnan, Johannes Gehrke, Database Management Systems, McGrawHill, 2000 (książka i slide y). 2. Lech Banachowski, Krzysztof Stencel, Systemy zarządzania bazami danych, Wydawnictwo PJWSTK, 2007. 3. Dokumentacja Oracle.
Część 1: Indeksy 2/56
Organizacje pliku z rekordami - przypomnienie Plik nieuporządkowany - heap Plik uporządkowany (posortowany) uprzywilejowane wyszukiwanie względem jednego porządku, dla którego jest budowany indeks. Rekordy danych są zapisane w indeksie. Plik haszowany - rozrzucenie rekordów do segmentów względem wartości funkcji haszującej od klucza. Uprzywilejowane wyszukiwanie względem wartości jednego klucza wyszukiwania. Tablica wielowymiarowa - rozrzucenie rekordów do komórek (segmentów) względem wartości wymiarów. Uprzywilejowane wyszukiwanie względem wartości kluczy wyszukiwania służących za wymiary. 3/56
Wyszukiwanie Najczęściej wykonywaną operacją w bazie danych jest wyszukiwanie danych. W najprostszej postaci: mając konkretną wartość poszukujemy rekordów, w których ta wartość występuje w danym polu. 4/56
Indeksy Plik nieuporządkowany umożliwia wyszukanie rekordu: mając (nie zmieniający się) wskaźnik albo fizyczny (adres) albo logiczny (wartość klucza głównego) do rekordu, lub przeglądając sekwencyjnie wszystkie rekordy w pliku. Często wyszukiwanie na podstawie wartości jednego lub więcej pól, np. wyznacz wszystkich studentów specjalizacji "BD", wyznacz wszystkich studentów mających < 20 lat, u w kolejności alfabetycznej nazwisk studentów. Indeksy są strukturami danych, których celem jest wspomaganie szybkiego wyszukiwania wartości pól i rekordów zapisanych w tabelach. Takie samo znaczenie jak skorowidz (indeks) w książce! 5/56
Indeksy Klucz wyszukiwania dla indeksu wybrane pola rekordu, względem których ma się odbywać wyszukiwanie. Indeksy składają się z: pozycji danych u k* - oznacza pozycję danych o wartości klucza wyszukiwania k, pozycji indeksu, kierujących wyznaczeniem właściwej pozycji danych w oparciu o wartości klucza wyszukiwania. 6/56
Indeksy Plik danych zbiór rekordów danych. Plik indeksu zbiór pozycji danych i pozycji indeksu. Indeks wewnętrzny - plik indeksu zawiera w sobie plik danych. Pozycja danych k* = rekord danych. Indeks zewnętrzny plik indeksu jest rozłączny z plikiem danych. Pozycja danych k* zawiera id rekordu danych. Postacie: 1. <k, id rekordu z tą wartością klucza k> albo 2. <k, lista id rekordów z tą wartością klucza k> gdzie id to adres gdy rekord nie zmienia miejsca wpp jednoznaczna wartość go identyfikująca (np. wartość klucza). W pozycjach danych są czasami dodawane dodatkowe pola (included columns), umożliwiające realizację zapytań bez przechodzenia do pliku danych. 7/56
Klasyfikacje indeksów Gdy klucz wyszukiwania = klucz główny indeks główny wpp. indeks niegłówny. Indeks jednoznaczny - klucz wyszukiwania = klucz jednoznaczny (także dla pól z NULL). Indeks pogrupowany (clustered) - indeks wewnętrzny i plik danych posortowany według wartości klucza indeksu. 8/56
Indeks pogrupowany Pozycje danych = rekordy danych - są zapisane w kolejności uporządkowanej względem wartości klucza indeksu. W rezultacie, rekordy o tej samej wartości klucza lub zbliżonej znajdują się na tej samej stronie lub na kilku powiązanych. indeks rzadki indeks gęsty 9/56
Rodzaje indeksów wewnętrznych Plik danych posortowany wtedy indeks pogrupowany, albo Plik danych haszowany - wtedy indeks haszowany. 10/56
Złożone klucze wyszukiwania Złożone klucze wyszukiwania: kombinacja pól np <age,sal> Zapytanie równościowe: u age=20 and sal =75 Zapytanie zakresowe: u age < 20; age=20 and sal > 10 Porządek leksykograficzny 11,80 12,10 12,20 13,75 <age, sal> 10,12 20,12 75,13 80,11 <sal, age> name age sal bob cal 12 11 joe 12 Pozycje danych posortowane <sal,age> 10 80 20 sue 13 75 Rekordy danych posortowane <name> 11 12 12 13 <age> 10 20 75 80 <sal> Pozycje danych posortowane <sal> 11/56
Pseudo-wartość NULL Indeksowane są te wiersze, w których co najmniej jedna składowa klucza wyszukiwania indeksu nie jest NULL. Na przykład, za pomocą indeksu na kolumnie Comm nie można zrealizować wyszukiwania wierszy z nieokreśloną wartością w polu Comm: SELECT Ename FROM Emp WHERE Comm IS NULL; Można: WHERE Comm IS NOLL NULL; Ograniczenie to nie dotyczy indeksów bitmapowych. 12/56
Wyszukiwanie zakresowe plik posortowany "Wyznacz studentów ze średnią > 4.0" Gdy dane w pliku posortowanym typu spójny obszar stron: u wyszukiwanie binarne aby znaleźć pierwszego takiego studenta; u przejdź plik wypisując pozostałych takich studentów. Klucze w pliku indeksu k1< k2< <KN k1 najmniejszy klucz na stronie Page1, k2 na Page2,, itd. k1 k2 kn Jeden poziom pliku indeksu (spójny) Page 0 Page1 Page 2 Page N * Wyszukiwanie binarne na mniejszym pliku indeksu! * Plik danych może być zapisany na liście stron! Plik danych posortowany (niekoniecznie spójny obszar na dysku)
Strona indeksu Pozycja indeksu K < K 1. < K 2 m P 0 K 1 P 1 K 2 P 2 K m P m Statyczne drzewo ISAM (indexed sequential access method) Węzły wewnętrzne Liście Strony nadmiarowe Strony główne * W liściach - pozycje danych, w węzłach wewnętrznych pozycje indeksu.
Drzewo ISAM Korzeń 40 20 33 51 63 10* 15* 20* 27* 33* 37* 40* 46* 51* 55* 63* 97*
Wstawiamy 23*, 48*, 41*, 42*... Strony indeksu Korzeń 40 20 33 51 63 Strony danych główne 10* 15* 20* 27* 33* 37* 40* 46* 51* 55* 63* 97* Strony danych nadmiarowe 23* 48* 41* 42*
B+ drzewo Wstawianie/usuwanie w czasie rzędu log N; drzewo wyważone względem wysokości (N = # liści). Każdy węzeł zawiera d <= m <= 2d pozycji indeksu (dla korzenia 2 <= m <= 2d ). Parametr d - stopień drzewa. Zapytania równościowe i zakresowe. Pozycje indeksu Pozycje danych
B+ drzewo Wyszukiwanie zaczyna się w korzeniu a porównania klucza wyszukiwania prowadzą do liścia tak jak dla drzew ISAM. Wyszukiwanie 5*, 15*, >= 24*... Korzeń 13 17 24 30 2* 3* 5* 7* 14* 16* 19* 20* 22* 24* 27* 29* 33* 34* 38* 39* * Na podstawie wyniku wyszukiwania 15*, wiemy że 15* nie ma w drzewie
Drzewa B+ w praktyce Średni stopień d=100. Średnie zapełnienie: 67%. Typowa pojemność: Wysokość 4: 133 4 = 312,900,700 rekordów Wysokość 3: 133 3 = 2,352,637 rekordów Zwykle górne poziomy drzewa w cache: Poziom 1 = 1 strona = 8 KB Poziom 2 = 133 strony = 1 MB Poziom 3 = 17,689 strony = 133 MB *
Wstawienie pozycji danych do B+ drzewa Wyznacz odpowiedni liść L. Wstaw pozycję danych do L. Jeśli mieści się to koniec! Wpp, podziel L (na L i nowy węzeł L2) u u Rozdziel równo pozycje, skopiuj na wyższy poziom środkowy klucz (najmniejszy w prawym węźle L2). Do ojca L wstaw pozycję indeksu z tym kluczem i wskaźnikiem wskazującym na L2. W razie potrzeby powtórz krok podziału rekurencyjnie. Kroki podziału mogą dojść do korzenia i w rezultacie drzewo może zwiększyć swoją wysokość o jeden. *
Wstawianie 8* Podział liścia 5 Pozycja wstawiana do ojca jest kopiowana. 2* 3* 5* 7* 8* Podział węzła wewnętrznego 17 5 13 24 30 Pozycja wstawiana do ojca jest przesuwana w górę. *
Drzewo po wstawieniu 8* Korzeń 17 5 13 24 30 2* 3* 5* 7* 8* 14* 16* 19* 20* 22* 24* 27* 29* 33* 34* 38* 39* Poprzedni korzeń uległ podziałowi. Wysokość drzewa zwiększyła się o 1. *
Usunięcie pozycji danych z B+ drzewa Podobny algorytm jak przy wstawianiu. Opóźnione usuwanie przy użyciu operacji REBUILD. 23/56
B+ drzewo jako indeks Indeks może być wewnętrzny i wtedy pozycje danych pokrywają się z rekordami danych, tzn. rekordy danych są zapisywane w liściach B+ drzewa zgodnie z porządkiem klucza wyszukiwania. Z tego powodu jest to wtedy indeks pogrupowany. Albo indeks jest zewnętrzny i wtedy rekordy z danymi są przechowywane poza tym indeksem. 24/56
Strategia zastępowania buforów dla stron B+ drzewa LRU nie jest tu dobrą metodą! Korzeń drzewa i najwyższe poziomy dobrze jest trzymać cały czas w pamięci RAM! 25/56
Dodatkowe operacje na B+ drzewach Gdy ilość zajętego miejsca w drzewie spada poniżej pewnego progu (z powodu DELETE i UPDATE), możliwe: REBUILD utworzenie indeksu od nowa u usunięte rekordy pozostają fizycznie zapisane na stronie i są usuwane dopiero przy REBUILD. COALESCE połączenie ze sobą sąsiednich stron o zajętości poniżej 50%. Gdy na samym początku mamy dany duży zbiór rekordów, to wtedy zamiast powtarzania kolejnych operacji INSERT opłaca się zastosować algorytm BULK LOAD, którego działanie polega na posortowaniu pliku rekordów z danymi, a następnie dobudowaniu nad posortowanym ciągiem kolejnych poziomów indeksowych drzewa B+. 26/56
Podsumowanie drzew Zastosowanie indeksów o strukturze drzewa: Wyszukiwanie zakresowe Wyszukiwanie równościowe Sortowanie (np. ORDER BY) ISAM struktura statyczna: Modyfikowane są tylko liście Wymagane są strony nadmiarowe mogące istotnie pogorszyć działanie algorytmów. B+ drzewo struktura dynamiczna: W praktyce wysokość na ogół <=4
Indeks haszowany Ustalona alokacja stron głównych; alokowane dodatkowe strony nadmiarowe w razie potrzeby. h(k) np. = k mod M = segment do którego należy pozycja danych k* o kluczu wyszukiwania k (M = # segmentów). h(klucz) klucz h 0 1 M-1 Strony główne Strony nadmiarowe
Indeks haszowany z uporządkowanymi segmentami Wariant, w którym pozycje danych w każdym segmencie są uporządkowane względem wartości klucza wyszukiwania. 29/56
Podsumowanie haszowania Mogą powstać długie łańcuchy nadmiarowych stron co prowadzi do pogorszenia działania: konieczność okresowego wykonywania operacji REBUILD, istnieją dynamiczne wersje haszowania polegające na zwiększaniu liczby M segmentów przez ich podział. Indeksy haszowane dobre przy wyszukiwaniu równościowym. Nie wspomagają: wyszukiwania zakresowego, sortowania po kluczu wyszukiwania.
Dla jednej tabeli 1. Plik danych nieuporządkowany, wszystkie indeksy zewnętrzne Rekordy danych mają stałą lokalizację. W pozycjach danych indeksów znajdują się niezmienne wskaźniki do rekordów w pliku danych. Domyślny w Oracle. 31/56
2. Indeks wewnętrzny Dla jednej tabeli może być zbudowany tylko jeden indeks wewnętrzny i wiele indeksów zewnętrznych. W indeksie wewnętrznym postaci B+ drzewa rekordy z danymi są zapisywane w liściach. W indeksie wewnętrznym haszowanym rekordy z danymi są zapisywane na stronach segmentów tablicy haszowanej. Rekordy mogą być przesuwane między stronami co powoduje zmianę fizycznego wskaźnika do rekordu. W indeksach zewnętrznych zamiast adresu używa się wartości klucza indeksu wewnętrznego. Domyślny w SQL Serer. 32/56
2. Indeks wewnętrzny c.d. Wyszukiwanie rekordu według wartości klucza wyszukiwania indeksu zewnętrznego, wymaga przejścia dwóch indeksów: 1. najpierw zewnętrznego, w którym znajdujemy wartość klucza indeksu pogrupowanego odpowiadającego szukanemu rekordowi, 2. a następnie indeksu pogrupowanego, w którym w oparciu o wartość klucza wyszukiwania znajdujemy szukany rekord. 33/56
Indeks pogrupowany i niepogrupowany Jeśli istnieje indeks pogrupowany, wskaźnikami w pozycjach danych w indeksie niepogrupowanym są klucze indeksu pogrupowanego a nie adresy do rekordów. 34/56
Struktury danych wielowymiarowe tablica wielowymiarowa Siatka (ang. grid file) podział obszaru wyszukiwań, powiedzmy leżącego na płaszczyźnie, poziomymi i pionowymi liniami na prostokąty. Punkty trafiające do jednego prostokąta są zapisywane w jednym segmencie. Gdy segmenty się przepełnią, można albo stosować strony nadmiarowe albo podzielić prostokąty na mniejsze zwiększając liczbę segmentów. 35/56
Struktury danych wielowymiarowe Możliwość zastosowania funkcji haszowanej dla układu wartości np. H(1,...,n)=h1(1)&h2(2)&...&hn(n) u u Wartością funkcji haszującej dla układu wartości jest konkatenacja tekstowa wartości funkcji haszujących dla poszczególnych składowych. Wspiera wyszukiwanie rekordów również gdy tylko niektóre składowe i są określone. 36/56
Drzewa wielowymiarowe do przechowywania zbioru dowolnych obiektów geometrycznych R drzewa (prostokątne) są pewnym uogólnieniem B drzew do n wymiarów. Pozycją danych w takim drzewie jest para złożona z n-wymiarowej kostki: {(1,...,n): ai<=i<=bi dla 1<=i<=n} oraz adresu pewnego obiektu geometrycznego, który jest ograniczony tą kostką może to być punkt. Podobnie, pozycją indeksu jest taka kostka oraz wskaźnik do węzła niższego poziomu, w którego poddrzewie znajdują się wyłącznie obiekty zawarte w tej kostce. Kostki poddrzew danego węzła nie muszą być rozłączne. 37/56
Powtórzenie Plik z rekordami danych składowanie danych nieuporządkowany uporządkowany (posortowany) haszowany tablica wielowymiarowa Indeks wyszukiwanie danych, jednoznaczność kluczy, blokady główny, jednoznaczny wewnętrzny: pozycja danych = rekord danych zewnętrzny: pozycja danych = identyfikator rekordu danych pogrupowany wewnętrzny & plik danych uporządkowany według wartości klucza indeksu B+ drzewo tablica haszowana 38/56
Uwaga na słabą selektywność wyszukiwania po kolumnie! SELECT E.Ename FROM Emp E WHERE E.Job=:job ORDER BY E.Ename Zwykły indeks na E.Job może być zły ze względu na słabą selektywność wyszukiwania po stanowiskach E.Job! Ale: 1. Indeks haszowany z kluczem E.Job sortowane segmenty po E.Ename 2. Indeks pogrupowany: Emp(Job, ) 3. Strategia tylko-indeks. 39/56
Część 2 Sortowanie w bazie danych 40/56
Zastosowanie sortowania w bazach danych ORDER BY - dane są wymagane w pewnym porządku. Budowa indeksu - początkowego B+ drzewa dla wczytywanego zbioru rekordów. Złączanie tabel metodą Sort-merge. Realizacja DISTINCT, GROUP BY, UNION, EXCEPT alternatywą haszowanie. Problem: posortować 5GB danych za pomocą 100MB RAM
Sortowanie zewnętrzne (wielofazowe przez scalanie) Faza 0 sortowanie rekordów w ramach stron: Wczytaj stronę, posortuj ją, zapisz na dysku. Faza 1,2,3, itd: scalaj uporządkowane podpliki w większe uporządkowane podpliki aż cały plik zostanie uporządkowany. Wejście 1 Wyjście Dysk Wejście 2 Bufory w RAM Dysk Dwa strumienie uporządkowanych danych są scalane w jeden uporządkowany.
Przykład W każdej fazie odczytujemy i 3,4 6,2 9,4 8,7 5,6 3,1 2 3,4 2,6 4,9 7,8 5,6 1,3 2 Plik wejściowy Faza 0 zapisujemy każdą stronę w pliku. 2,3 4,6 4,7 8,9 1,3 5,6 2 Faza 1 Idea: Dziel i rządź: sortuj podpliki i je scalaj. Zamiast dwóch buforów można użyć więcej. 2,3 4,4 6,7 8,9 1,2 3,5 6 Faza 2 Faza 3 Praktycznie liczba faz <=3. 1,2 2,3 3,4 4,5 6,6 7,8 9
Sortowanie za pomocą B+ drzewa Warunek: Tabela ma indeks na B+ drzewie względem kolumn sortowania. Idea: Przejść po liściach indeksu. Czy jest to dobra metoda? Przypadki: Indeks pogrupowany Bardzo dobra! Indeks niepogrupowany Może być bardzo zła!
Indeks pogrupowany Od korzenia przejdź do skrajnie lewego liścia a następnie sekwencyjnie w prawo po liściach. Indeks Pozycje danych Rekordy danych w pozycjach danych! Liniowy względem liczby stron * Zawsze lepsze od sortowania zewnętrznego!
Indeks niepogrupowany Ogólnie, jedna operacja We/Wy na rekord danych! Liniowy względem liczby rekordów, nie stron! Indeks Pozycje danych Rekordy danych
Dodatek: Realizacja indeksów w Oracle 47/56
Indeksy w Oracle Indeks oparty na B+ drzewie Indeks pogrupowany oparty na B+ drzewie Indeks oparty na B+ drzewie z odwróconymi składowymi kluczy Indeks oparty na klastrze jednej lub więcej tabel (B+ drzewo lub hasz), w szczególności indeks haszowany Indeks ze składowymi określonymi za pomocą wyrażeń Indeks bitmapowy w implementacji są dwa B+ drzewa: wartość -> wektor 0-1 oraz numer pozycji w wektorze -> wiersz 48/56
Plik nieuporządkowany Pozycja danych składa się z wartości indeksowanych kolumn oraz z identyfikatora ROWID wiersza w tabeli - określającego fizyczne położenie danego wiersza na dysku. Umożliwia: znalezienie wiersza w oparciu o wartość klucza wyszukiwania, realizację zapytań zakresowych. Przy wykonywaniu zapytania system używa indeksu zewnętrznego opartego na B+ drzewie tylko wtedy gdy jest zapewniona wystarczająca selektywność wyszukiwania, powiedzmy zwracane zostaje co najwyżej 5 do 10% wszystkich rekordów w pliku. Indeks zewnętrzny oparty na B+ drzewie jest domyślnie tworzony dla każdego klucza głównego i jednoznacznego. 49/56
Indeks pogrupowany oparty na B+ drzewie Indeks pogrupowany główny. Pozycja danych = wiersz tabeli. Jest zapewniony szybki dostęp do wierszy przez wartości klucza głównego. Wiersze nie posiadają swoich identyfikatorów ROWID identyfikacja wierszy przebiega wyłącznie przez wartości klucza głównego; w pozostałych indeksach wynikiem wyszukania jest wartość klucza głównego. 50/56
Indeks pogrupowany oparty na B+ Załóżmy, że chcemy dokonywać analizy klientów wyszukując klientów mieszkających w określonym mieście. Miasta(Id_miasta, Nazwa_miasta) drzewie Klienci(Id_miasta, Id_klien_w_miescie, Nazwisko, Hobby, Wiek) Jeśli na kluczu głównym tabeli Klienci jest założony indeks pogrupowany, istotnie można przyśpieszyć wykonywanie zapytań w rodzaju: SELECT K.Nazwisko, K.Hobby FROM Klienci K INNER JOIN Miasta M ON K.Id_miasta = M.Id_miasta WHERE M.Nazwa_miasta = 'WARSZAWA' AND K.Wiek BETWEEN 18 and 25; Zastosowanie klucza głównego złożonego! 51/56
Tworzenie indeksu opartego na B+ drzewie połączonego z tabelą CREATE TABLE Klienci( Id_miasta INTEGER, Id_klien_w_miescie INTEGER, Nazwisko VARCHAR2(80), Hobby VARCHAR2(20), Wiek INTEGER, CONSTRAINT Klienci_pk PRIMARY KEY (Id_miasta, Id_klien_w_miescie), CONSTRAINT Klienci_fk FOREIGN KEY(Id_miasta) REFERENCES Miasta ) ORGANIZATION INDEX; 52/56
Indeks haszowany Przykład zastosowania indeksu haszowanego dla klucza głównego Id_konta tabeli: Konta(Id_konta, Saldo, Imie, Nazwisko, Adres) Zakładamy, że tabela Konta zawiera bardzo dużo wierszy oraz że często wielu kasjerów w banku równocześnie wykonuje zapytanie: SELECT * FROM Konta k WHERE k.id_konta = :numer; 53/56
Tworzenie indeksu haszowanego CREATE CLUSTER Klast_konta(Id_konta INTEGER) SIZE 512 SINGLE TABLE HASHKEYS 100003 HASH IS mod(id_konta, 100003); Następnie definiujemy tabelę Konta: CREATE TABLE Konta(Id_konta INTEGER PRIMARY KEY, Saldo NUMBER, Imie VARCHAR2(20), Nazwisko VARCHAR2(50), Adres VARCHAR2(70)) CLUSTER Klast_konta(Id_konta); 54/56
Tworzenie indeksu haszowanego Indeks klastra jest tworzony automatycznie. Parametr SIZE w definicji klastra określa rozmiar bloku - ilość miejsca w bajtach przeznaczoną do zapisania rekordów z tą samą wartością klucza klastra. Domyślną wartością (gdy brak klauzuli SIZE) jest rozmiar strony dyskowej. Jeśli nie wszystkie wiersze dla danej wartości klucza klastra mieszczą się w jednym bloku, są zapisywane na liście nadmiarowych bloków. Poprzez dobór odpowiedniej wartości SIZE można spowodować, że wszystkie rekordy z daną wartością klucza znajdą się na tej samej lub tylko na kilku stronach dyskowych. 55/56
Sortowanie segmentów haszowanych CREATE CLUSTER call_detail_cluster (telno NUMBER, call_timestamp NUMBER SORT, call_duration NUMBER SORT) HASHKEYS 10000 HASH IS telno SIZE 256; CREATE TABLE call_detail (telno NUMBER, call_timestamp NUMBER SORT, call_duration NUMBER SORT, other_info VARCHAR2(30) ) CLUSTER call_detail_cluster (telno, call_timestamp, call_duration); Wyświetlić rozmowy wykonane z danego numeru w kolejności od najwcześniejszej (billing). SELECT * FROM call_detail WHERE telno = 6505551212 ORDER BY call_timestamp; 56/56