1
Dziedzina systemóv~ baz danych
Czytelnicy niniejszej książki dowiedzą się, w jaki sposób można skutecznie korzystać z systemów zarządzania bazami danych, a zwłaszcza jak projektować bazy danych oraz progamować operacje na danych. Bieżący rozdzial posłuży do wprowadzenia ważnych pojęć dotyczących baz danych. Po krótkim rysie historycznym sprecyzujemy, czym w istocie różnią się systemy baz danych od innych rodzajów oprogamowania. Opiszemy również podstawowe elementy implementowania systemów zarządzania bazami danych, potrzebne przy ich stosowaniu. Zrozumienie owych „zakulisowych" zagadnień staje się ważne wówczas, gdy trzeba stwierdzić, dlaczego baza danych została zaprojektowana tak, a nie inaczej, albo dlaczego sposoby wykonywania operacji na danych podlegają ograniczeniom. W końcu dokonamy przeglądu pewnych kierunków w tej dziedzinie, takich na przyklad jak progumowanie obiektowe, które być może są już znane czytelnikom, ale są niezbędne do zrozumienia treści następnych rozdzialów.
1.1. Ewolucj a systemów baz danych
Czym jest baza danych? Baza danych nie jest w istocie rzeczy niczym więcej niż zbiorem danych istniejącym przez długi czas, często przez wiele lat. W potocznym rozumieniu termin baza danych odnosi się do zbioru danych zorganizowanego przez system zarzc~dzania baza danych, który nazywa się również skrótowo systemem DBMS (database management system) lub po prostu systemem baz danych. Oto, czego oczekuje się od systemu DBMS:
1. Umożliwienia użytkownikowi utworzenia nowej bazy danych i określenia jej schematu (logicznej struktury danych) za pomocą specjalizowanego języka definiowania danych (data-definition language).
2. Udostępnienia użytkownikowi możliwości tworzenia zapytań (query) o dane (czasem określane po polsku również mianem kwerend) oraz
ZO 1. DZIEDZINA SYSTEMÓW BAZ DANYCH
aktualizowania danych, za pomocą odpowiedniego języka nazywanego językiem zapytań (query language) lub językiem operowania danymi (data - manipulation language).
3. Zapewnienia możliwości przechowywania ogromnej ilości danych, mierzonej w gigabajtach lub nawet większych jednostkach, przez długi czas, chroniąc je przed przypadkowym, lub niepowolanym dostępem, a także umożliwiając efektywny dostęp do danych z poziomu języka zapytań i operacji na danych.
4. Sterowania jednoczesnym dostępem do danych przez wielu użytkowników, z zapewnieniem bezkolizyjności oraz ochrony danych przed przypadkowym uszkodzeniem.
1.1.1. Pierwsze systemy zarządzania bazami danych
Pierwsze profesjonalne systemy zarządzania bazami danych pojawiły się na rynku pod koniec lat sześćdziesiątych. Początkowo sprowadzały się one do zwykłych systemów plików. Systemy plików mają niektóre z wlaściwości wymienionych powyżej (punkt 3): umożliwiają na przykkad przechowywanie danych przez dlugi czas i zapewniają przestrzeń do magazynowania dużej ilości danych. Jednak w zasadzie systemy plików nie gwarantują, że dane nie zostaną utracone, jeśli nie zostanie utworzona ich kopia zapasowa, ani nie gwarantują szybkiego dostępu do tych danych, których lokalizacja nie jest dokładnie znana.
Systemy plików ponadto nie stwarzają możliwości opisanych w punkcie 2, gdyż nie są wyposażone w język zapytań. Ich przydatność do sprostania zaleceniom z punktu 1 - opis schematu bazy danych - sprowadza się do udostępnienia mechanizmu struktury katalogowej. Systemy plików nie spełniają również wymagań z punktu 4, ponieważ kilku użytkowników lub kilka procesów może mieć jednocześnie dostęp do tego samego pliku. Z kolei system plików w zasadzie nie dostarcza mechanizmów, które pozwalają unikać sytuacji takich, że dwóch użytkowników jednocześnie modyfikuje ten sam plik, ale zmiany wprowadzane przez jednego z nich nie zostaną w pliku zapisane.
Pierwsze poważne implementacje DBMS dawały możliwość rozłożenia zbioru danych na niewielkie elementy, co z kolei gwarantowało łatwość tworzenia zapytań i modyfikacji. Poniżej przedstawiamy opisy kilku zastosowań takich systemów DBMS.
Systemy rezerwacji miejsc lotniczych
W tym przypadku zbiór danych składa się z następujących elementów:
1. Rezerwacja na określone rejs dokonana przez konkretnego klienta, obejmująca a~umer fotela i upodobania dotyczące posiłków.
l.l. EWOLUCJA SYSTEMÓW BAZ DANYCH
21
2. Informacje dotyczące rejsu: miejsce odlotu, port przeznaczenia, czas odlotu i czas przylotu oraz na przykład typ samolotu.
3. Dane o dostępności biletów i wariantach cen.
Typowe zapytania dotyczą rejsów między określonymi portami w określonym terminie, dostępności i cenach odpowiednich miejsc. Rutynowe aktualizacje obejmują rezerwację rejsu dla klienta, przydział miejsca i określenie upodobań kulinarnych. Systemy muszą zapewniać jednoczesny dostęp do danych w dowolnej chwili dla wielu agencji. Nie mogą również dopuścić do tego, by na przykład dwie agencje przydzieliły to samo miejsce w tym samym rejsie, ani do utraty informacji w przypadku usterek systemu komputerowego.
Systemy bankowe
Wśród elementów danych wyróżnia się: nazwiska i adresy klientów, konta, kredyty i bilans na koncie oraz powiązanie klienta z jego kontem i kredytem, tzn. autoryzowanie podpisem danego konta. Wśród operacji dominują zapytania o bilans na koncie i, jeszcze częstsze od nich, aktualizacje dotyczą ce wpływów i wypłat z kont.
Podobnie jak w przypadku systemów rezerwacji lotniczych, tak i tutaj należy oczekiwać, że wielu klientów i urzędników będzie jednocześnie korzystać z danych i aktualizować je (poprzez bankomaty). Tego typu transakcje nie mogą być pod żadnym pozorem utracone, tutaj błędów się nie toleruje. Na przykład, w chwili gdy bankomat dokona wypłaty, system musi ten fakt zanotować, nawet jeśli w tym momencie nastąpi awaria zasilania. Tak samo jest niedopuszczalne, żeby zaksięgować wypłatę, a nie wypłacić pieniędzy, bo akurat zasilanie zostało przerwane. Poprawna obsługa podobnych operacji nie jest łatwa ani oczywista i należy jej implementację uznać za jedno z najbardziej znaczących osiągnięć architektury systemu DBMS.
Dokumentowanie działania przedsiębiorstw
Wiele wczesnych wdrożeń dotyczy dokumentowania działalności przedsiębiorstw, zapisu każdej sprzedaży, wpłat i wypłat z kont czy też ewidencji pracowników: ich nazwisk, adresów, płac, jak również nagród, podatków itp. Zapytania dotyczą raportowania wysokości wpływów do kasy czy też wypłat pracowniczych. Każda sprzedaż, zakup, rachunek, płatność, zwolnienie lub zatrudnienie wyrażają się przez aktualizację danych.
Pierwsze systemy DBMS, powstałe z systemów plików, wymagały od użytkownika tworzenia obrazów danych zgodnych z ich strukturą w bazie. W tych systemach używano kilku różnych modeli danych do opisu struktury danych w bazie, prym wiodły tutaj modele hierarchiczny, inaczej drzewiasty, oraz sieciowy, nazywany także grafowym. Ten drugi miał nawet standard utworzony pod koniec lat sześćdziesiątych w raporcie komitetu CODASYL
ZZ 1. DZIEDZINA SYSTEMÓW BAZ DANYCH
(Komitet ds. systemów i języków danych (Committee on Data System and languages)~. W podrozdziale 2.7 Czytelnicy będą mieli sposobność poznać oba te modele: hierarchiczny i sieciowy, mimo że obecnie mają one już tylko historyczne znaczenie.
Kłopot z używaniem pierwszych modeli i systemów polegał na tym, że nie dawały one możliwości korzystania z języków zapytań wysokiego poziomu. Na przykład język opracowany przez CODASYL dostarczał instrukcje poruszania się pomiędzy elementami danych w postaci grafa wskaźników do nich. Trzeba było nie lada wysiłku, aby napisać w tym języku program, obsługujący nawet bardzo proste zapytania.
1.1.2. Relacyjne systemy baz danych
Systemy baz danych zmieniły się znacznie od ukazania się w roku 1970 słynnego artykułu Teda Codda"*. Propozycja Codda polegała na umożliwieniu prezentowania użytkownikowi danych w postaci tabel, nazywanych relacjami. Wewnątrz systemu natomiast mogła powstawać zlożona struktura danych, która gwarantowala uzyskiwanie blyskawicznych wyników dla różnorodnych zapytań. Ale w przeciwieństwie do wczesnych baz danych użytkownik relacyjnych baz danych nic nie musiał wiedzieć o tej wewnętrznej strukturze. Zapytania można było wyrażać w języku bardzo wysokiego poziomu, co w znaczny sposób podniosło wydajność pracy programistów baz danych. Model relacyjny systemów baz danych dominuje w naszym podręczniku, poczynając od rozdzialu 3, gdzie pojawia się opis podstawowych pojęć. Od rozdzialu 5 rozpocznie się nauka języka SQL (structured query language język zapytań strukturalnych), najważniejszego języka zapytań dla modeli relacyjnych. Krótkie wprowadzenie do relacji pozwoli Czytelnikowi uchwycić prostotę tego modelu, a przedstawiony poniżej przykład w SQL obrazuje to, że model relacyjny umożliwia naturalny zapis zapytania bez szczególowej „nawigacji" w bazie danych.
PRZYKŁAD 1.1
Relacje są przedstawione w postaci tabel. W nagłówkach kolumn znajdują się atrybuty, które opisują zawartość kolumn. Oto przykład relacji nazwanej Konta, służącej do księgowania kont bankowych, ich bilansu i typu.
Nr konta Bilans Typ 12345 1000,00 oszczędnościowy 67890 2846,92 rozliczeniowy
" CODASYL Data Base Task Group Apil 1971 Report, ACM, New York.
"` Codd E.F.: A relational model for (arge shared data banks. Comna. ACH, 13 : 6, s. 377
387.
1.1. EWOLUCJA SYSTEMÓW BAZ DANYCH 23
W nagłówkach kolumn występują trzy atrybuty: nr konta, bilans oraz typ. Pod nazwami atrybutów znajdują się wiersze nazywane knotkami. Zostały wypisane dwie knotki, wielokropki umieszczone pod nimi sugerują, że jest o wiele więcej knotek, po jednej dla każdego konta w banku. Pierwsza knotka informuje o tym, że na koncie o numerze 12345 zgromadzono tysiąc dolarów i że jest to rachunek oszczędnościowy. Dane z drugiej knotki dotyczą konta rozliczeniowego o numerze 67890 z bilansem w wysokości 2846,92$.
Załóżmy, że chcemy z bazy danych otrzymać informację o bieżącym bilansie konta nr 67890. Zapytanie w SQL wygląda następująco:
SELECT bilans EROM Konta WHERE nr konta = 67890;
W następnym przykładzie pokażemy, w jaki sposób zapytać o konta oszczędnościowe z bilansem ujemnym:
SELECT nr konta EROM Konta
WHERE typ = `oszczędnościowe' AND bilans < 0;
Nie oczekujemy oczywiście, że podanie dwóch przykładów wystarczy, aby Czytelnicy stali się ekspertami w zakresie programowania w SQL. Powinno to jednak umożliwić zrozumienie istoty zapisu instrukcji select-fromwhere tego języka. W zasadzie w przykładach tych zleca się systemowi baz danych przeprowadzenie następujących operacji:
1. Sprawdzenie wszystkich knotek relacji Konta, wymienionej w klauzuli EROM.
2. Wyszukanie wszystkich tych knotek, które spełniają warunek opisany w klauzuli WHERE.
3. Sformułowanie odpowiedzi zawierającej wybrane knotki z uwzględnieniem atrybutów wskazanych w klauzuli sELECT.
W praktyce system musi zapewnić „optymalizację" zapytania i znaleźć wydajny sposób znalezienia odpowiedzi, nawet jeśli relacje, których dotyczy zapytanie, są bardzo obszerne.
0
Najwcześniej na rynku pojawiły się relacyjne i „przedrelacyjne" systemy DBMS, produkowane w firmie IBM. Poza tym, aby tworzyć i sprzedawać relacyjne systemy DBMS, specjalnie powstawały nowe firmy. Niektóre z nich znajdują się obecnie wśród największych producentów oprogramowania na świecie.
24 I DZIEDZINA SYSTEMÓW BAZ DANYCH
1.1.3. Coraz mniejsze systemy
Początkowo systemy zarządzania bazami danych wymagały ogromnych komputerów, a same były bardzo obszerne i bardzo kosztowne. Wymagania co do wielkości wynikały stąd, że tylko wielki system komputerowy mógł pomieścić gigabajty danych. Obecnie gigabajt mieści się na jednym dysku i uruchomienie systemu DBMS na komputerze osobistym stało się zupełnie realne. Dlatego też relacyjne systemy baz danych pojawiają się na bardzo małych maszynach i zaczynają być tak samo popularnym narzędziem w zastosowaniach technik komputerowych, jak arkusze kalkulacyjne i edytory tekstu.
1.1.4. Coraz większe systemy
Patrząc na to z innej strony, gigabajt nie jest to dużo danych. Bazy danych w przedsiębiorstwach zajmują często setki gigabajtów. Z czasem, gdy nośniki pamięci stają się coraz tańsze, ludzie wynajdują coraz to nowe powody do przechowywania coraz większej ilości danych. Na przykład w sieciach handlu detalicznego często przechowuje się terabajty (1000 gigabajtów, co oznacza 1012 bajtów) lub większe ilości danych, dokumentujących w długim okresie każdą sprzedaż (do celów planowania inwestycji; więcej na ten temat będziemy mieli do powiedzenia w części 1.3.4). Bazy danych nie są już przeznaczone do przechowywania wyłącznie prostych typów danych, takich jak całkowite lub znakowe. Obecnie można zapamiętywać obrazy, dźwięk, filmy i wiele innych typów danych, które zajmują względnie dużo pamięci. Na przykład film video wyświetlany przez godzinę zajmuje około jednego gigabajta pamięci. Oczekuje się, że do roku 2000 pojawią się bazy danych ze zdjęciami satelitarnymi zajmujące petabajty (1000 terabajtów równe 1015 bajtów) pamięci.
Obsługa tak wielkich baz danych wymaga zaawansowanych technik. Na przykład średniej wielkości bazy danych przechowuje się obecnie na dyskach, zorganizowanych w tak zwane macierze dyskowe i określanych jako pamięci drugiego poziomu (w odróżnieniu od pamięci operacyjnej, która nazywa się pamięcic~ pierwszego poziomu). Można nawet pokusić się o stwierdzenie, że tym co najbardziej różni systemy baz danych od innych rodzajów oprogramowania jest fakt, że z założenia dane z bazy danych są zbyt obszerne, by zmieścić się w pamięci operacyjnej i muszą być przez prawie cały czas przechowywane na dysku. Istnieją dwa trendy, które stwarzają perspektywę uzyskiwania szybkiego dostępu do dużych zasobów danych.
Pamięć trzeciego poziomu
Największe współczesne bazy danych potrzebują czegoś więcej niż dyski. Urządzenia trzeciego poziomu, z których każde może przechować terabajt danych, wymagają dłuższego czasu dostępu do wybranego elementu, niż ma
1.2. ARCHITEKTURA SYSTEMU DBMS 2S
to miejsce w przypadku dysku. Dostęp do danych zapisanych na dysku zajmuje od 10 do 20 milisekund, natomiast dostęp do danych zapisanych w pamięciach trzeciego poziomu może trwać kilka sekund. Technika korzystania z urządzeń trzeciego poziomu obejmuje między innymi transport nośnika z zapamiętanymi danymi do urządzenia czytającego. Komunikację tego typu zapewnia zastosowanie pewnego rodzaju robotów transportowych.
Na przykład pamięć trzeciego poziomu może być zorganizowana z wykorzystaniem kompaktów (CD) jako nośników. W takim przypadku ruchome ramię wybiera wkaściwy dysk, podnosi go, przemieszcza do czytnika, a potem umieszcza w napędzie.
Obliczenia równoległe
Zdolność przechowania bardzo dużych woluminów danych jest ważna, lecz jej znaczenie byłoby znikome, gdyby nie można byto uzyskać szybkiego dostępu do danych. Dlatego też wielkie bazy danych wymagają również postępu w zakresie szybkości przetwarzania. Poważne przyspieszenie uzyskuje się dzięki zastosowaniu struktur indeksowych, które omawiamy w p. 1.2.1 oraz 5.7.7. Innym sposobem przetworzenia większej liczby danych w określonym czasie jest skorzystanie z równoleglości. Równoległość może się przejawiać na kilka sposobów.
Na przykład tempo czytania danych z dysku jest względnie niskie, kilka megabajtów na sekundę, lecz proces ten można przyspieszyć, stosując kilka dysków i czytając z nich jednocześnie (nawet jeśli dane na początku znajdują się w pamięci trzeciego poziomu, to są one skladowane na dysku, zanim będą osiągalne dla systemu DBMS). Takie dyski mogą stanowić część maszyny równoległej lub mogą być skladnikiem systemu rozproszonego, w którym wiele maszyn, każda odpowiedzialna za pewien fragment bazy danych, komunikuje się ze sobą poprzez szybką sieć.
Oczywiście ani zdolność do szybkiego przesyłania danych, ani zdolność przechowywania wielkiej liczby danych, nie gwarantują szybkiego generowania wyników dla zapytań. Należy nadal korzystać z algorytmów dekomponujących zapytania w taki sposób, aby zasoby komputerów równoleglych lub sieci komputerowych mogly być efektywnie użyte. Stąd też zarządzanie równoleglością i przetwarzaniem rozproszonym w dużych bazach danych stanowi obszar intensywnych badan.
1.2. Architektura systemu DBMS
W tej części przedstawimy zarys struktury typowego systemu zarządzania bazami danych. Przyjrzymy się również sposobom przetwarzania zapytań i innych operacji na bazach danych. W końcu rozważymy problemy wynika
2C) 1. DZIEDZINA SYSTEMÓW BAZ DANYCH
jące przy projektowaniu DBMS, przeznaczonych do obsługi wielkich zasobów danych i szybkiego przetwarzania zapytań. Jednak technologia implementowania DBMS nie jest tematem wiodącym w naszej książce, dlatego też skoncentrujemy uwagę na tym, w jaki sposób projektować bazy danych i korzystać z nich w efektywny sposób.
1.2.1. Przegląd składowych systemu DBMS
Na rysunku 1.1 przedstawiono najbardziej istotne fragmenty DBMS. Na samym dole widzimy element reprezentujący miejsce składowania danych. Zauważmy, że ten element służy nie tylko do zapisu danych, ale także metadanych, które opisują strukturę danych. Na przykład, jeśli rozważany DBMS jest relacyjny, to metadane obejmują nazwy relacji, nazwy atrybutów relacji i typy poszczególnych atrybutów (np. całkowity lub znakowy o długości nie większej niż 20).
Modyfikacje Za tania Aktualizacje schematu py
Procesor zapytań
Moduł zarzadzania transakcjami
Moduł zarządzania pamięcia
Dane Metadane
RYSUNEK 1.1
Główne elementy systemu DBMS
Często system DBMS obsługuje indeksy danych. Indeks jest taką strukturą danych, która pomaga w szybkim odnajdywaniu właściwych danych, a posługuje się przy tym ich wartościami; najbardziej popularny przykład indeksu
1.2. ARCHITEKTURA SYSTEMU DBMS 2%
umożliwia odnalezienie właściwej krotki relacji, mającej zadane wartości pewnych atrybutów. Na przykład relacja obejmująca numery kont i bilans może mieć indeks założony na numerach kont, wówczas odnalezienie bilansu konta o podanym numerze odbywa się błyskawicznie. Indeksy są przechowywane razem z danymi, a informacja o tym, który atrybut ma założone indeksy, należy do metadanych.
Jak są zaimplementowane indeksy
Z wykładu dotyczącego struktur danych Czytelnicy zapewne dowiedzieli się, że bardzo skutecznym sposobem tworzenia indeksu są tablice haszowania. Były one szeroko stosowane we wczesnych systemach DBMS. Obecnie najbardziej rozpowszechnioną strukturą danych są B-drzewa, gdzie „B" oznacza „balanced - zrównoważony". B-drzewo stanowi uogólnienie zrównoważonego binarnego drzewa przeszukiwań. Różnica polega na tym, że każdy wierzchołek drzewa binarnego ma co najwyżej dwóch potomków, a wierzchołki B-drzewa mogą mieć ich więcej. Z założenia B-drzewo jest zapisywane na dysku, zamiast w pamięci operacyjnej, i jest tak projektowane, aby jeden wierzchołek zajmował cały jeden blok na dysku. Ponieważ w typowym systemie bloki dyskowe zajmują około 2I2 bajtów (4096 bajtów), więc daje to sposobność zapisania setek wskaźników do potomków w jednym bloku B-drzewa. Stąd też przeszukiwanie B-drzewa rzadko kiedy wymaga więcej niż trzech dostępów do dysku.
Rzeczywisty koszt przeszukiwania dysku w zasadzie jest proporcjonalny do liczby dostępów do dysku. Stąd też przeszukiwania B-drzewa, które zwykle polegają na sprawdzeniu trzech bloków na dysku, są znacznie bardziej wydajne niż przeszukiwania drzew binarnych, które wymagają zazwyczaj odszukania wierzchołków znajdujących się w wielu rożnych blokach na dysku. Różnica między B-drzewami a drzewami binarnymi jest jednym z wielu przykładów tego, że struktury danych bardzo odpowiednie dla danych przechowywanych na dysku są zupełnie inne niż struktury danych używane w algorytmach operujących wyłącznie na danych, znajdujących się w pamięci operacyjnej.
Na rysunku 1.1 można także dostrzec moduł zarzc~dzania pami4cict, który ma za zadanie wybierać właściwe dane z pamięci i w razie potrzeby dostosować je do wymagań modułów z wyższych poziomów systemu. Widać tam także składową, którą nazwaliśmy modułem przetwarzania zapytań, mimo że taka nazwa może wprowadzać w błąd, bowiem obsługuje on nie tylko zapytania, ale również aktualizacje danych oraz metadanych. Jego zadanie polega na znalezieniu najlepszego sposobu wykonania zadanych operacji i na wydaniu poleceń do modułu zarządzania pamięcią, który je wykona.
Modus zarzcidzania transakcjami odpowiada za spójność systemu. Musi on gwarantować, że kilka jednocześnie przetwarzanych zapytań nie będzie
28
1. DZIEDZINA SYSTEMÓW BAZ DANYCH
sobie wzajemnie przeszkadzać oraz że żadne dane nie zostaną utracone, nawet jeśli nastąpi awaria systemu. Współdziała on z modułem obsługi zapytań, ponieważ musi mieć dostęp do szczegółów dotyczących tych danych, na których przetwarza się bieżące zapytania (w celu uniknięcia konfliktów). Może się zdarzyć, że część przetwarzania będzie musiała zostać opóźniona, aby nie powstał konflikt.
U góry rysunku 1.1 można zobaczyć trzy rodzaje wejść do systemu DBMS: 1. Zapytania. Są to pytania o dane. Mogą one być sformułowane dwojako:
a) Poprzez interfejs zapytań bezpośrednich. Na przykład relacyjny DBMS umożliwia wprowadzenie zapytań w SQL, które są następnie przekazywane do modułu przetwarzania zapytań, który z kolei tworzy odpowiedź.
b) Poprzez interfejsy programów użytkowych. Typowy DBMS umożliwia programiście utworzenie programu użytkowego, który poprzez wywołania procedur DBMS tworzy zapytania do bazy danych. Na przykład agent posługujący się systemem rezerwacji lotniczych uruchamia program użytkowy, który tworzy zapytanie do bazy danych dotyczące dostępności rejsów. Zapytania mogą być formułowane dzięki specjalizowanemu interfejsowi, który na ogół składa się z formularzy z pustymi polami, przeznaczonymi do wypełnienia konkretnymi danymi, np. nazwą miasta, terminem itp. Nie można w ten sposób zadać zupełnie dowolnego pytania, ale jest znacznie łatwiej sformułować typowe zapytanie poprzez taki interfejs, niż formułować zapytanie bezpośrednio w języku SQL.
2. Aktualizacje. Są to operacje zmiany danych. Tak jak w przypadku zapytań można je wprowadzać do systemu poprzez interfejsy zapytań bezpośrednich lub poprzez interfejsy programów użytkowych.
3. Modyfikacje schematu. Polecenia tego rodzaju wydaje specjalnie uprawniona osoba nazywana administratorem bazy danych, której wolno zmieniać schemat bazy danych i tworzyć nowe bazy danych. Na przykład, jeśli agencje rządowe wezwą banki do udokumentowania wypłaty odsetek zgodnie z numerami ubezpieczenia społecznego klientów, to bank może zażądać dodania do relacji opisującej klientów nowego atrybutu o nazwie nrUbezpieczenia.
1.2.2. Modus zarządzania pamięcią
W prostych systemach baz danych moduł zarządzania pamięcią może być tym samym co system plików podstawowego systemu operacyjnego. Jednak na ogół, przynajmniej w pewnych okolicznościach, DBMS bezpośrednio zarządza przestrzenią na dysku, co jest podyktowane względami
1.2. ARCHITEKTURA SYSTEMU DBMS 29
efektywności. Moduł zarządzania pamięcią składa się z dwóch części: modułu zarządzania buforami oraz modułu zarządzania plikami.
1. Modul zarządzania plikami przechowuje dane o miejscu zapisania plików na dysku i na polecenie modułu zarządzania buforami przesyła zawartość bloku lub bloków, gdzie jest zapamiętany żądany plik. Proszę sobie przypomnieć, że dyski zazwyczaj są zorganizowane w postaci bloków dyskowych, czyli spójnych obszarów o stosunkowo dużej pojemności 2'2 lub 2'4 bajtów (od 4000 do 16000 bajtów).
2. Moduł zarządzania buforami obsługuje pamięć operacyjną. Moduł zarządzania plikami przekazuje bloki danych z dysku, a moduł zarządzania buforami wybiera w pamięci operacyjnej strony, które zostaną przydzielone dla wybranych bloków. Blok z dysku może być przez chwilę przechowany w pamięci operacyjnej, ale musi zostać przesłany z powrotem na dysk, gdy tylko pojawi się potrzeba zapisania w to miejsce pamięci operacyjnej innego bloku. Powrót bloku na dysk może nastąpić również w wyniku żądania modułu obsługi transakcji (zob. p. 1.2.4).
1.2.3. Module zarządzania zapytaniami
Zadaniem modułu zarządzania zapytaniami jest przekształcenie zapytania lub operacji na bazie danych, wyrażanych zazwyczaj w języku bardzo wysokiego poziomu (np. jako zapytanie SQL), w ciąg poleceń żądających dostarczenia określonych danych, takich jak konkretne krotki zadanej relacji lub fragmenty indeksu relacji. Często najtrudniejszą operacją przetwarzania zapytań jest optymalizacja zapytania, tzn. wybór dobrego planu pytania lub ciągu poleceń do systemu zarządzania pamięcią, którego wykonanie wygeneruje właściwą odpowiedź w możliwie najkrótszym czasie.
PRZYKŁAD 1.2
Załóżmy, że bank opracował bazę danych z dwiema relacjami:
1. Klienci: jest to tabela, która zawiera nazwiska klientów banku, ich numer polisy ubezpieczenia społecznego oraz adres.
2. Konta: jest to tabela zawierająca dane o kontach, obejmujące numer konta, bilans oraz numer polisy ubezpieczenia społecznego właściciela konta. Warto zauważyć, że każde konto ma głównego właściciela, którego numer polisy służy celom podatkowym; mogą istnieć jeszcze inni właściciele konta, ale na podstawie tych dwóch relacji uie można o nich uzyskać informacji.
Załóżmy również, że zostało utworzone zapytanie „znaleźć bilanse tych wszystkich kont, których głównym właścicielem jest Anna Nowak". Moduł
3O 1. DZIEDZINA SYSTEMÓW BAZ DANYCH
zarządzania zapytaniami musi określić dla tego zapytania plan, którego wykonanie doprowadzi do utworzenia poprawnej odpowiedzi. Im mniej jest kroków w trakcie tworzenia odpowiedzi, tym plan jest lepszy. W zasadzie kosztowne są te operacje, które prowadzą do kopiowania bloków dysku do pamięci lub kopiowania stron pamięci operacyjnej na dysk, angażujące moduł zarządzania pamięcią. Z tego powodu warto przy określaniu kosztu planu zapytania brać pod uwagę tylko operacje przesyłania danych między pamięciami operacyjną a pomocniczą.
Utworzenie odpowiedzi wymaga przeszukania relacji Klienci w celu odnalezienia numeru polisy należącej do Anny Nowak (zakładamy, że w bazie występuje tylko jedna klientka o takim nazwisku, jednak w praktyce może ich być więcej). Potem trzeba przeszukać relację Konta i odnaleźć wszystkie konta o właściwym numerze polisy, a następnie wydrukować bilanse z tych kont.
Prosty, ale kosztowny plan polega na przejrzeniu wszystkich krotek (wierszy) relacji Klienci, aż do odnalezienia tej, zawierającej nazwisko Anna Nowak. Średni koszt takiej operacji wymaga przejrzenia połowy krotek zanim napotkamy właściwą. Ponieważ bank ma wielu klientów, więc tabela Klienci zajmuje dużo bloków dysku i koszt będzie wysoki. A odnalezienie numeru polisy Anny Nowak nie kończy zadania. Teraz trzeba przeglądać krotki relacji Konta i wybrać te wszystkie, w których występuje znaleziony uprzednio numer polisy. W typowym banku jest wiele kont, a zatem relacja Konta również musi zajmować wiele bloków dysku. Sprawdzenie ich wszystkich musi też być kosztowne.
Jeżeli jednak został założony indeks dla relacji Klienci na atrybucie nazwiska, to istnieje lepszy plan. Zamiast przeszukiwać całą relację Klienci skorzystamy z indeksu, który wskaże blok, zawierający krotkę właściwą dla Anny Nowak. Jak już wspomniano w p. 1.2.1, odnalezienie właściwego bloku za pomocą typowego indeksu, zbudowanego jako B-drzewo, wymaga przejrzenia trzech bloków indeksu. Jeszcze jeden dostęp do dysku pozwala otrzymać krotkę odpowiadającą Annie Nowak.
Oczywiście nadal pozostaje do zrobienia drugi krok: znalezienie kont z odpowiednim numerem polisy w relacji Konta. Zazwyczaj wymaga to wielokrotnego dostępu do dysku. Jeśli jednak dla relacji Konta założono indeks na atrybucie numer polisy, to możemy według tego indeksu przeszukiwać tylko te bloki, w których występują poszukiwane krotki, zawierające odnaleziony uprzednio numer polisy. Trzeba wówczas wykonać 2 lub 3 operacje dyskowe, przeglądając indeks, podobnie jak w przypadku indeksowanego dostępu do relacji Klienci. Jeśli wszystkie potrzebne krotki sąw różnych blokach dysku, to trzeba dostać się do każdego z tych bloków. Ale prawdopodobnie jedna oso
" W rzeczywistości ponieważ każde przeszukania B-drzewa wymaga przęjrzenia korzeuia, więc ten blok przeważnie jest na stalce w pamięci operacyjnej, zajmując jedną stronę pamięci. Stąd też tak naprawdę skorzystanie z tego rodzaju indeksu wymaga zazwyczaj tylko dwóch operacji dostępu do dysku.
1.2. ARCHITEKTURA SYSTEMU DBMS 3 1
ba nie ma zbyt wielu kont, więc całe działanie sprowadzi się pewnie do niewielu operacji dyskowych. Jeśli zostały założone oba indeksy, to można utworzyć odpowiedź na zapytanie, angażując w to od 6 do 10 operacji dostępu. Jeśli któryś z nich, albo żaden, nie istnieje i musimy korzystać z gorszych planów zapytań, to operacj i dostępu do dysku może być potrzebnych setki, a nawet tysiące, ponieważ trzeba sprawdzać wówczas całą, obszerną relację.
0
Na podstawie przykładu 1.2 można odnieść wrażenie, że optymalizacja zapytań polega wyłącznie na korzystaniu z indeksów, o ile takie istnieją. Jednak w rzeczywistości takie procedury są dużo bardziej złożone. Skomplikowane zapytanie umożliwia na przykład zmianę kolejności wykonywania operacji, co z kolei stwarza możliwość utworzenia wielu planów wykonania takiego zapytania, ich liczba może rosnąć w stosunku do wielkości zapytania nawet wykładniczo. Często mamy do wyboru dwa indeksy i trzeba wybrać któryś z nich. Rozwiązania na temat implementacji tego ważnego elementu DBMS są jednak już poza zakresem naszej książki
1.2.4. Moduł zarządzania transakcjami
Jak już wspomnieliśmy w podrozdziale 1.l, DBMS musi wykonanie niektórych operacji na bazie danych objąć specjalnymi gwarancjami. Stwierdziliśmy wówczas, że wynik pewnych operacji nie może zaginąć, nawet w przypadku awarii systemu. Typowy DBMS stwarza użytkownikowi sposobność łączenia jednego lub więcej zapytań, bądź modyfikacji, w transakcję, która stanowi nieformalną grupę operacji przeznaczonych do wykonania razem w jednym ciągu, jako duża operacja jednostkowa.
Często system baz danych umożliwia jednoczesne przetwarzanie transakcji, na przykład coś się może dziać jednocześnie na wielu bankomatach. Poprawność i kompletność przeprowadzenia wszystkich transakcji gwarantuje w systemie DBMS modul zarzddzania transakcjami. „Poprawność" przeprowadzenia transakcji bardziej szczegółowo opisują poniższe właściwości, inicjały ich angielskich nazw tworzą słowo ACID. A oto te właściwości:
Niepodzielność (atomicity). Żądamy, aby albo cała transakcja została przeprowadzona, albo wcale - żaden z jej elementów nie zostanie uwzględniony. Na przykład podjęcie pieniędzy z bankomatu i powiązany z tym zapis wypłaty na koncie klienta muszą stanowić jedną niepodzielną transakcję. Nie można zaakceptować wypłaty pieniędzy i niezapisania tego faktu na koncie klienta, nie można również zgodzić się na to, by zapisać wypłatę, a nie wypłacić pieniędzy.
Spójność (consistency). Baza danych musi stwarzać warunki spójności, dane muszą zaspakajać nasze oczekiwania. Na przykład jeden
32 1. DZIEDZINA SYSTEMOW BAZ DANYCH
z warunków spójności dla bazy danych linii lotniczych określa, że jedno miejsce w konkretnym rejsie nie zostanie przydzielone dwóm różnym klientom. Taki warunek może zostać naruszony tylko na króciutką chwilę podczas przetwarzania transakcji, gdy przydziela się miejsca dla pasażerów, ale moduł zarządzania transakcjami musi zagwarantować, że po zakończeniu przetwarzania transakcji baza danych spełnia wszystkie warunki niesprzeczności.
Izolacja (isolation). Gdy dwie transakcje są przetwarzane jednocześnie, ich działania nie mogą na siebie wzajemnie wpływać. To oznacza, że w wyniku jednoczesnego działania dwóch transakcji nie może zdarzyć się nic, co nie stało by się, gdyby działały jedna po drugiej, sekwencyjnie. Na przykład, jeśli dwie agencje lotnicze sprzedają bilety na ten sam rejs, a zostało tylko jedno miejsce, to tylko jedno żądanie powinno zostać obsłużone, a drugie nie. Jednoczesność przeprowadzania transakcji nie może spowodować sprzedaży tego samego biletu dwóm różnym osobom.
Trwalość (durability). Jeśli transakcja się zakończy, to jej wynik nie może zostać utracony z powodu awarii systemu, nawet jeśli awaria następuje natychmiast po zakończeniu transakcji.
Sposób implementowania transakcji, który zapewnia zachowanie właściwości ACID, może być tematem zupełnie oddzielnej książki i nie zamierzamy nawet próbować pomieścić tutaj stosownych treści. W części 7.2 opisujemy natomiast w jaki sposób, w języku SQL, określić transakcje i właściwe dla nich operacje oraz jakie gwarancje uzyskuje programista, który korzysta z możliwości grupowania operacji w transakcje. Poza tym bardzo skrótowo przedstawimy tam popularne rozwiązania techniczce zapewniające transakcjom właściwości ACID.
Granulacja blokad
Systemy DBMS różnią się rozmiarami elementów danych, które są poddawane blokowaniu. Na przykład można zastosować blokadę na poziomie pojedynczych krotek, poszczególnych bloków dysku lub nawet na poziomie całych relacji. Im większe są elementy, które blokujemy, tym bardziej jest prawdopodobne, że jedna transakcja będzie musiała czekać na inną, nawet jeśli nie potrzebują dostępu do tych samych danych. Jednakże, im mniejsze są blokowane elementy, tym większy i bardziej skomplikowany musi być mechanizm obsługi blokad.
Blokady
Najczęstszą przyczyną nierozłączności transakcji jest konieczność czytania lub zapisu tych samych danych w bazie danych. Na przykład, jeśli dwie
1.2. ARCHITEKTURA SYSTEMU DBMS 3 3
transakcje próbują jednocześnie zapisać nowe wartości bilansu na tym samym koncie, to jedna z transakcji zniszczy zapis wykonany przez drugą z nich. Dlatego w większości DBMS modul zarządzania transakcjami potrafi zablokować element, którego dotyczy wykonywana właśnie transakcja. Po zalożeniu blokady dane są niedostępne dla innych transakcji. Mechanizm blokad w naszym przykładzie poskutkuje w ten sposób, że transakcja, dla której zostanie zablokowana krotka z bilansem konta 12345, będzie mogła odczytywać wartości tej krotki i zmieniać je, zanim druga transakcja uzyska dostęp do tej krotki. Druga transakcja uzyska zatem dostęp do nowych wartości bilansu, co oznacza, że transakcje nie przeszkadzają sobie.
Logi
Modul zarządzania transakcjami dokumentuje wszystkie operacje, tzn. rozpoczęcie każdej transakcji, zmiany w bazie danych dokonane przez transakcje oraz zakończenie transakcji. Zapis taki nazywa się logiem. Log jest przechowywany w pamięci stalej, tzn. na nośniku danych takim jak dysk, który zapewni przetrwanie danych w przypadku awarii zasilania. Zasadnicze przetwarzanie transakcji odbywa się w ulotnej pamięci operacyjnej, ale dane o przebiegu jej wykonania są natychmiast zapisywane na dysku. Log wszystkich operacji jest ważnym czynnikiem zapewniającym systemowi trwałość.
Zatwierdzanie transakcji
Z powodu konieczności zagwarantowania transakcjom niepodzielności i trwalości są one najpierw wstępnie przetwarzane, co oznacza, że aktualizacje są wyliczane, ale nie są zapisywane w bazie danych. W chwili gdy transakcja kończy działanie, jest gotowa do zatwierdzenia, zmiany są kopiowane do logu. Log jest pierwszym zapisem dokonanym na dysku przez transakcję. Dopiero potem następująaktualizacje danych.
Nawet jeśli awaria systemu nastąpi w trakcie przetwarzania operacji, to po przywróceniu funkcjonowania można przeczytać logi i stwierdzić, które zmiany trzeba jeszcze wprowadzić do bazy danych. Jeśli awaria systemu nastąpi przed zapamiętaniem logu, trzeba powtórzyć całą transakcję, co zagwarantuje, że na przykład nie przydzielono dwukrotnie tego samego miejsca w samolocie lub nie obciążono dwukrotnie konta tą samą wypłatą.
1.2.5. Architektura klient-serwer
W wielu współczesnych systemach informatycznych stosuje się architekturę klient-serwer, gdzie polecenie jednego procesu (klienta) jest przesylane do innego procesu (serwera) i tam obslugiwane. Systemy baz danych nie stanowią wyjątku i często wprowadza się podzial składowych z rys. 1.1 na proces serwem i jeden lub kilka procesów klienckich.
34 1. DZIEDZINA SYSTEMÓW BAZ DANYCH
W najprostszych systemach typu klient/serwer cały DBMS stanowi serwer, a tylko interfejs zapytań współdziała z użytkownikiem przy definiowaniu zapytania i przesyła je jako klient do obsługi serwerowi. Na przykład relacyjne systemy baz danych zlecenia klienta do serwera przedstawiają w formie programów w języku SQL. Serwer bazy danych przesyła następnie odpowiedź w postaci tabeli lub formularza do klienta. Związek między klientem a serwerem może się bardziej komplikować, szczególnie jeśli odpowiedzi są ekstremalnie wielkie. Więcej na ten temat będzie można dowiedzieć się po przeczytaniu p. 1.3.3. Od kiedy dostęp do serwera, z powodu wielu jednocześnie działających użytkowników baz danych, stal się wąskim gardłem, zarysowała się tendencja rozbudowy zadań klienckich.
1.3. Przysz~ość systemów baz danych
Obecnie w dziedzinie baz danych obserwuje się wiele nowych prądów, które powodują zmiany kierunków jej rozwoju. Niektóre z nich są związane z nowymi technologiami, takimi jak na przykład programowanie zorientowane obiektowo, więzy i wyzwalacze lub dane multimedialne, albo ze zjawiskami, które całkiem zmieniają naturę DBMS, jak stało się za przyczyną światowej sieci WWW. Inne prądy, takie jak hurtownie danych lub integracja danych, są związane z nowymi aplikacjami. W bieżącej części przedstawimy główne trendy rozwoju przyszłych systemów baz danych.
1.3.1. Typy, klasy i obiekty
Programowanie zorientowane obiektowo jest oceniane w szerokich kręgach jako narzędzie, które pozwala lepiej organizować program i implementować zdecydowanie bardziej niezawodne oprogramowanie. Spopularyzował je język Smalltalk, ale naprawdę rozgłos nadał mu rozwój języka C++ i migracja większości oprogramowania tworzonego uprzednio w języku C do postaci zapisanej w C++. Teraz uwagę przykuwa inny język zorientowany obiektowo, służący do uruchamiania programów poprzez sieć WWW - język Java. Świat baz danych wykazuje również zainteresowanie podejściem obiektowym i kilka firm sprzedaje systemy DBMS opatrzone napisem „zorientowane obiektowo". Następny fragment rozdziału poświęcimy przeglą dowi tego, co kryje się pod pojęciem obiektowości.
System typów
,fęzyki programowania zorientowane obiektowo oferują użytkownikowi bogaty zestaw typów. Zaczynając od typów podstawowych, obejmujących
1.3. PRZYSZŁOŚĆ SYSTEMÓW BAZ DANYCH 3 S
zazwyczaj liczby całkowite i rzeczywiste, wartości logiczne i znaki, można tworzyć nowe typy pochodne, korzystając w tym celu z konstruktorów typów. Zazwyczaj konstruktory umożliwiajątworzenie:
1. Rekordów (record structures). Zalóżmy, że dane są typy Tl, T2, ..., T„, a odpowiadające im pola (nazywane w Smalltalku zmiennymi indywidualnymi) mają nazwy f~, fZ, ..., fn. Można wówczas utworzyć nowy typ przez strukturę rekordu zlożonego z n składowych. I-ta skladowa jest typu T;, a można się do niej odwolać przez nazwę f. W językach C i C++ rekordy są deklarowane słowem „struct".
2. Kolekcji (collection types). Z danego typu T można tworzyć typy pochodne, stosując w tym celu operator kolekcji. W różnych językach operatory kolekcji różnią się, ale część z nich, na przykład tablice, listy i zbiory, jest charakterystyczna dla wielu języków. Oznacza to, że gdy typ całkowity jest określony jako bazowy, to można tworzyć na przykład następujące kolekcje: „tablica typu calkowitego", „lista typu calkowitego" lub „zbiór typu calkowitego".
3. Referencje (references types). Referencja (odniesienie) do typu T jest typem, którego wartości są odpowiednie do przechowywania wartości typu T. W językach C i C++ referencja jest „wskaźnikiem" do wartości, to znaczy jest to miejsce, w którym przechowywany jest adres wirtualny wskazywanej wartości. Model wskaźników zazwyczaj wystarcza do zrozumienia referencji. Jednak w systemach baz danych, tam gdzie dane przechowuje się na wielu dyskach i często bywają one rozproszone pomiędzy wiele komputerów, pojęcie referencji jest bardziej złożone niż wskaźnika. Obejmuje ono wówczas również systemową nazwę komputera, numer dysku, numer bloku, a w końcu pozycję w bloku, gdzie jest przechowywana wskazywana wartość.
Oczywiście operatory kolekcji i rekordów można wielokrotnie na siebie nakładać i tworzyć w ten sposób coraz bardziej złożone typy. Na przykład można zdefiniować rekord, którego pierwsza składowa, o nazwie klient, jest typu znakowego, druga natomiast jest zbiorem elementów typu całkowitego (czyli typem pochodnym) i nazywa się konta. Taki typ jest bardzo odpowiedni przy wiązaniu informacji o klientach i numerach ich kont bankowych.
Klasy i obiekty
Klasa stanowi polączenie typu oraz jednej lub kilku funkcji albo procedur, (nazywanych metodami), które można wykonywać na obiektach danej klasy. Obiekty klasy są albo wartościami danego typu (tzw. obiekty niemutowalne), albo zmiennymi o wartościach danego typu (tzw. obiekty mutowalne). Na przykład, jeśli zdefiniujemy klasę C, której typem jest zbiór wartości całkowitych, to f 2, S, 7) będzie niemutowalnym obiektem klasy C, podczas gdy
3 C) 1. DZIEDZINA SYSTEMÓW BAZ DANYCH
zmienna s może otrzymać typ C, a tylko jej chwilowa wartość będzie równa konkretnemu zbiorowi {2, 5, 7}.
Identyfikator obiektu
Zakłada się, że obiektom przysluguje identyfikator (object identity OID). Żadne dwa obiekty nie mogą mieć tego samego identyfikatora, ani żaden obiekt nie może mieć więcej niż jednego identyfikatora. Identyfikatorem jest wartość, którą przyjmuje odniesienie do obiektu. Można często utożsamiać identyfikator obiektu ze wskaźnikiem do obiektu w pamięci wirtualnej, ale, jak już wspomniano przy okazji omawiania typu odniesienie, identyfkator może w przypadku bazy danych być czymś znacznie bardziej złożonym: ciągiem bitów pozwalającym na dokładne umiejscowienie obiektu w pamięci drugiego lub trzeciego poziomu na jednym z wielu różnych komputerów. A poza tym, ponieważ dane mają charakter trwały, więc identyfikator musi mieć właściwą wartość przez cały czas ich istnienia.
Metody
Z klasami są zazwyczaj powiązane pewne funkcje, często nazywane metodami. Metoda klasy C ma co najmniej jeden argument, jest nim obiekt klasy C. Jeśli na przyklad zdefiniujemy klasę, której typem jest „zbiór liczb calkowitych", to można mieć metodę slużącą do wyliczenia zbioru potęgowego danego zbioru, sumy dwóch zbiorów lub taką, która zwraca wartość logiczną służącą do sprawdzenia, czy zbiór nie jest pusty.
Abstrakcyjne typy danych
W wielu przypadkach klasy są także abstrakcyjnymi typami danych, co znaczy, że ograniczają dostęp do obiektu tylko dla metod, które są specjalnie dla tych klas zdefiniowane, a z kolei tylko te metody umożliwiają aktualizowanie obiektów danej klasy. Taka właściwość nazywa się hermetyzacja. Jej przestrzeganie gwarantuje, że obiekty danej klasy nie będą zaktualizowane w sposób, którego nie zaplanowal implementator. Ocenia się, że jest to obecnie podstawowy mechanizm tworzenia niezawodnego oprogramowania.
Hierarchie klas
Można zadeklarować klasę C jako podklasę innej klasy D. W takim przypadku klasa C dziedziczy wszystkie cechy klasy D, to znaczy jej typ i wszystkie funkcje. Jednak klasa C może mieć także dodatkowe elementy. Można na przykład definiować nowe metody dla obiektów klasy C i mogą to być zupeknie nowe, dodatkowe metody albo takie, które zastąpią metody klasy D. Istnieją nawet pewne sposoby rozszerzenia typu klasy D. Główny z nich dotyczy rekordów
13. PRZYSZŁOŚĆ SYSTEMÓW BAZ DANYCH 3
i polega na tym, że jeśli podstawowym typem klasy D jest rekord, to można dołączyć nowe pola, które będą występować tylko w obiektach typu C.
PRZYKŁAD 1.3
Rozważmy klasę obiektów kont bankowych. Nieformalnie możemy opisać typ tej klasy w następujący sposób:
CLASS Konto = {Nrkonta: integer;
bilans: real; właściciel: REF Klient;
Typem klasy Konta jest rekord z trzema polami: calkowitym numerem konta, bilansem typu rzeczywistego oraz właścicielem, który wskazuje na obiekt klasy Klient (innej klasy potrzebnej do obiektowego projektu bazy kont bankowych, ale której typu nie będziemy definiować).
Dlaczego obiekty?
Programowanie zorientowane obiektowo oferuje szereg możliwości o szczególnym znaczeniu dla baz danych.
Dzięki rozbudowanemu systemowi typów możemy mieć do czynienia z formatem danych bardziej naturalnym niż relacje lub wcześniejsze modele danych. Należy zauważyć, że model relacyjny ma dość ograniczony system typów. Relacje są zbiorami rekordów, a rekordy są złożone z pól (nazywanych w relacyjnym modelu atrybutami), których typ musi należeć do jednego z typów podstawowych.
Dzięki klasom i ich hierarchiom można wielokrotnie korzystać z gotowych fragmentów oprogramowania znacznie częściej niż występuje to w przypadku konwencjonalnych systemów.
Dzięki abstrakcyjnym typom danych można chronić dane przed niewlaściwym użyciem poprzez ograniczenie dostępu do nich, który staje się możliwy tylko za pośrednictwem pewnych specjalnie w tym celu zaprojektowanych funkcji.
Można by zdefiniować także jakieś metody dla tej klasy. Niech to będzie na przykład metoda
wpłata( a: Konto, m: real)
która powoduje dodanie do wartości bilansu obiektu a typu Konto wartości m.
13. PRZYSZŁOŚĆ SYSTEMÓW BAZ DANYCH 3%
i polega na tym, że jeśli podstawowym typem klasy D jest rekord, to można dołączyć nowe pola, które będą występować tylko w obiektach typu C.
PRZYKŁAD 1.3
Rozważmy klasę obiektów kont bankowych. Nieformalnie możemy opisać typ tej klasy w następujący sposób:
CLASS Konto = {Nrkonta: integer; bilans: real; właściciel: REF Klient;
Typem klasy Konta jest rekord z trzema polami: całkowitym numerem konta, bilansem typu rzeczywistego oraz właścicielem, który wskazuje na obiekt klasy Klient (innej klasy potrzebnej do obiektowego projektu bazy kont bankowych, ale której typu nie będziemy definiować).
Dlaczego obiekty?
Programowanie zorientowane obiektowo oferuje szereg możliwości o szczególnym znaczeniu dla baz danych.
Dzięki rozbudowanemu systemowi typów możemy mieć do czynienia z formatem danych bardziej naturalnym niż relacje lub wcześniejsze modele danych. Należy zauważyć, że model relacyjny ma dość ograniczony system typów. Relacje są zbiorami rekordów, a rekordy są złożone z pól (nazywanych w relacyjnym modelu atrybutami), których typ musi należeć do jednego z typów podstawowych.
Dzięki klasom i ich hierarchiom można wielokrotnie korzystać z gotowych fragmentów oprogramowania znacznie częściej niż występuje to w przypadku konwencjonalnych systemów.
Dzięki abstrakcyjnym typom danych można chronić dane przed niewłaściwym użyciem poprzez ograniczenie dostępu do nich, który staje się możliwy tylko za pośrednictwem pewnych specjalnie w tym celu zaprojektowanych funkcji.
Można by zdefiniować także jakieś metody dla tej klasy. Niech to będzie na przykład metoda
wpłata( a: Konto, m: real)
która powoduje dodanie do wartości bilansu obiektu a typu Konto wartości m.
3 g 1. DZIEDZINA SYSTEMÓW BAZ, DANYCH
W końcu zażyczymy sobie kilku podklas klasy Konta. Na przykład konto depozytów terminowych może mieć dodatkowe pole termin, w którym zapisze się datę, po upływie której właściciel będzie mógł dokonywać wypłat. Dla tej podklasy, nazwanej na przykład DepozytTerminowy, można zdefiniować metodę:
kara (a: DepozytTerminowy)
która dla konta a wylicza obciążenie karne wynikłe z tytułu zbyt wczesnej wypłaty, a które jest możliwe do wyliczenia na podstawie daty bieżącej oraz wartości pola termin obiektu a. Datę bieżącąotrzymuje się z systemu operacyjnego komputera.
0
Zorientowane obiektowo aspekty baz danych będziemy omawiać bardzo obszernie w niniejszej książce. W podrozdziale 2.1 wprowadzimy zorientowany obiektowo język projektowania baz danych ODL (object-oriented database-design language). Z kolei rozdział 8 poświęciliśmy zorientowanym obiektowo językom zapytań, zarówno językowi OQL, który staje się standardem dla baz danych zorientowanych obiektowo, jak i obiektowym właściwościom języka SQL - standardu zapytań w relacyjnych bazach danych.
1.3.2. Więzy i wyzwalacze
Obecnie w bazach danych daje się zaobserwować szerokie zastosowanie elementów aktywnych w systemach komercyjnych. Przez „aktywność" elementu rozumiemy, że dana składowa bazy danych jest cały czas dostępna i jest gotowa do wykonania zawsze wtedy, kiedy staje się to właściwe. W bazach danych można wyróżnić dwa rodzaje elementów aktywnych:
1. Więzy (constraints). Są to funkcje logiczne, które zawsze powinny mieć wartość prawda. W bankowej bazie danych moglibyśmy na przykład umieścić wymaganie, określające, że wartość bilansu na koncie nie może być mniejsza niż 0. Wówczas aktualizacja polegająca na wypłacie z konta kwoty większej niż wartość zapisana w bilansie zostałaby zabroniona przez DBMS, jako naruszająca wymaganie określone przez więzy.
2. Wyzwalacze (triggers). Wyzwalacz stanowi kawałek kodu, który czeka na zajście zdarzenia; zdarzenia mogąpolegać na wprowadzeniu lub usunięciu pewnego rodzaju danych. Po zajściu zdarzenia zostaje wykonany, lub inaczej mówiąc wyzwolony, ciąg akcji. Na przykład w systemie rezerwacji lotów może wystąpić reguła, wyzwalająca pewne akcje wówczas, gdy zostaje odwołany jakiś rejs. Część wyko
1.3. PRZYSZŁOŚĆ SYSTEMÓW BAZ DANYCH
39
nywalna reguły może polegać na przetworzeniu zapytania o numery telefoniczne pasażerów odwoływanego rejsu po to, by można ich było powiadomić. Bardziej skomplikowane przetwarzanie może polegać na automatycznej rezerwacji w połączeniach alternatywnych.
Elementy aktywne nie są nowym pomysłem. Występowały już w języku PL/I pod nazwą „ON-conditions". Pojawiały się również przez wiele lat w systemach sztucznej inteligencji, a także mają wiele wspólnego z „demonami" występującymi w systemach operacyjnych. Jednak efektywna implementacja elementów aktywnych staje się poważnym problemem technicznym w przypadkach, gdy trzeba operować na dużych zbiorach danych lub gdy samych elementów aktywnych zaczyna być dużo. Z tego powodu elementy aktywne nie pojawiły się w systemie DBMS aż do początku lat dziewięćdziesiątych. Omówimy je w rozdziale 6.
1.3.3. Dane multimedialne
Inny ważny trend w dziedzinie systemów baz danych dotyczy danych multimedialnych. Słowem „multimedia" określamy dane reprezentujące pewne sygnały. Zazwyczaj dane multimedialne obejmują film, dźwięk, sygnały radarowe, obrazy satelitarne oraz rozmaicie kodowane obrazy i dokumenty. Wspólną cechą tych wszystkich form jest ich rozmiar, nie tylko znacznie większy niż danych tradycyjnych - typu całkowitego, napisów znakowych 0 określonej długości itp., ale także bardzo rozmaity.
Duża objętość danych multimedialnych wymusiła rozwój systemów DBMS w kilku różnych kierunkach. Na przykład zupełnie inne operacje przetwarza się na danych multimedialnych niż na danych tradycyjnych. Zatem operacja wyszukania kont bankowych z ujemnym bilansem polega na porównywaniu wartości bilansu z liczbą rzeczywistą 0,0. Nie jest natomiast wykonalne przeszukanie bazy danych zawierającej zdjęcia w celu wyszukania twarzy, która „wygląda jak" pewien określony obraz. Dlatego w systemie DBMS musiał powstać mechanizm umożliwiający użytkownikom dołączanie własnych funkcji, które mogą operować na danych multimedialnych. Do takich rozszerzeń często stosuje się podejście zorientowane obiektowo, nawet w systemach relacyjnych.
Rozmiary obiektów multimedialnych, konieczność zorganizowania pamięci dla obiektów i krotek zajmujących gigabajty pamięci, lub nawet większe jej obszary, wymuszają również zmiany w modułach zarządzania pamięcią. Jednym z wielu kkopotliwych zadań jest utworzenie odpowiedzi na zapytanie. W zwykkych relacyjnych systemach odpowiedź jest zbiorem krotek. Klient otrzymuje taką odpowiedź w całości od serwera.
Wyobraźmy sobie jednak, że odpowiedź jest wideoklipem o objętości gigabajta. Serwer nie jest w stanie przekazać takich danych klientowi w całości.
4O 1. DZIEDZINA SYSTEMÓW BAZ DANYCH
Trwałoby to zbyt długo i uniemożliwiło serwerowi obsługę innych zadań. Poza tym klient może potrzebować tylko fragmentu filmu, ale nie może dokładnie określić czego chce, zanim nie zobaczy początku. Trzeci powód konieczności nowego podejścia do obsługi zadań w przypadku danych multimedialnych wynika stąd, że nawet jeśli klient potrzebuje całego wideoklipu po to na przykład, by odtworzyć go na ekranie, wystarczy dostarczać go w kawałkach o ustalonej długości rytmicznie w ciągu około godziny (tyle mniej więcej czasu trwa wyświetlenie filmu skompresowanego w jednym gigabajcie). Dlatego system zarządzania pamięcią w przypadku multimedialnych baz danych musi być przygotowany do przesyłania odpowiedzi w trybie interakcyjnym, przesyłania właściwych fragmentów odpowiedzi na żądania klienta albo w ustalonych odstępach czasu.
1.3.4. Integracja danych
Z czasem, gdy informacja staje się coraz ważniejsza w pracy i zabawie, odkrywamy coraz nowe sposoby korzystania z istniejących źródeł danych. Rozważmy na przykład firmę, która chce wprowadzić katalog, dostępny przez WWW, po to, by klienci mogli bezpośrednio przeglądać produkty i wysyłać zamówienia. Wielka firma ma wiele oddziałów. Każdy oddział może mieć własną bazę danych, zupełnie niezależnie od innych oddziałów. Oddziały mogą korzystać z różnych systemów DBMS, różnych struktur danych, a nawet różnych nazw określających te same rzeczy albo tych samych terminów do określenia różnych rzeczy.
PRZYKŁAD 1.4
Rozważmy firmę z wieloma oddziałami, produkującą dyski do komputerów. Katalog jednego z oddziałów takiej firmy może opisywać szybkość dostępu liczbą obrotów na sekundę, a inny liczbą obrotów na minutę. Jeszcze inny oddział mógł w ogóle zaniedbać podania tej wielkości. Oddział produkcji dyskietek może posługiwać się w odniesieniu do swoich produktów terminem dysk i oddział produkujący dyski twarde też określa swoje towary mianem dysków. Liczba ścieżek na dysku twardym może. zostać opatrzona słowem „ścieżki" w jednym oddziale, a słowem „cylindry" w innym.
0
Centralna koordynacja nie zawsze skutkuje. Oddziały mogły zainwestować dużo pieniędzy w swoje bazy danych znacznie wcześniej, niż w ogóle dostrzeżono problem integracji danych między oddziałami. Poza tym jakiś oddział, który mógł właśnie ostatnio zostać włączony do firmy, dotychczas występował jako samodzielne przedsiębiorstwo. Z tych i wielu innych powodów tzw. spadkowych baz danych nie można w łatwy sposób zastąpić. Dlatego firma musi, ponad spadkowymi bazami danych, tworzyć nowe struktury,
1.4. UKŁAD KSIĄŻKI 4I
umożliwiające jednolity sposób przedstawienia klientom tych wszystkich danych, które dotyczą ogółu dostarczanych towarów i usług.
Jeden z rozpowszechnionych sposobów polega na tworzeniu hurtowni danych, baz centralnych, do których kopiuje się, po uprzednim przetłumaczeniu, dane z baz „spadkowych". Hurtownie danych są aktualizowane zgodnie ze zmianami, wprowadzanymi do baz spadkowych, ale nie dzieje się to natychmiast. Najczęściej hurtownie rekonstruuje się w nocy, gdy bazy spadkowe są mniej obciążone przetwarzaniem.
Dzięki takiemu rozwiązaniu spadkowe bazy danych mogą nadal działać zgodnie ze swoim przeznaczeniem. Natomiast nowe funkcje, takie jak na przykład utrzymywanie katalogów w sieci, spełnia hurtownia danych. Często obserwujemy również korzystanie z hurtowni danych do analiz i planowania. Na przykład analitycy przedsiębiorstwa mogą tworzyć zapytania do hurtowni danych, których wyniki ułatwią określenie trendu sprzedaży, a to z kolei umożliwi stworzenie lepszego planu produkcji i inwestycji. Hurtownie danych służą także przy wybieraniu danych (data mining), poszukiwaniu pośród danych ciekawych i niezwykłych wzorców i słyszy się stwierdzenia, że zastosowanie odkrytych w ten sposób wzorców dało efekt w postaci zwiększenia sprzedaży.
1.4. Układ książki
Zakres zagadnień związanych z bazami danych można podzielić na trzy obszerne grupy:
1. Projektowanie baz danych. Jak zbudować użyteczną bazę danych? Jakie rodzaje danych umieszcza się w bazach danych? Jaka jest struktura danych? Jakie założenia należy przyjmować o typach i wartościach poszczególnych elementów w bazie danych? W jaki sposób te elementy są ze sobą powiązane?
2. Programowanie baz danych. W jaki sposób wyrazić zapytania i inne operacje na bazie danych? W jaki sposób skorzystać z innych właściwości baz danych, na przykład z wyzwalaczy lub transakcji?
3. Implementacja baz danych. Jak zbudować DBMS, uwzględniając takie zagadnienia jak przetwarzanie zapytań, przetwarzanie transakcji oraz efektywny system zarządzania pamięcią?
Implementacje baz danych stanowią główny segment rynku oprogramowania, dlatego liczba osób zatrudnionych przy projektowaniu i korzystaniu z baz danych znacznie przewyższa liczbę tych, którzy budują systemy baz danych. Niniejsza książka jest pomyślana jako podręcznik do pierwszego wykładu z baz danych, więc właściwym wydaje się skupienie uwagi na
42 I. DZIEDZINA SYSTEMÓW BAZ DANYCH
pierwszych dwóch grupach zagadnień: projektowaniu i programowaniu. W tym rozdziale próbowaliśmy przelotnie spojrzeć na trzecią grupę - implementację - ale już nie powrócimy do tych zagadnień. W pozostałych rozdziałach pogrupowaliśmy tematy dotyczące projektowania i programowania.
1.4.1. Proj ektowanie
Rozdziały 2 i 3 dotyczą projektowania. Rozdział 2 zaczynamy od przedstawienia formalizmu wysokiego poziomu, przeznaczonego do opisu projektów baz danych. Pierwszy z nich, ODL (język definiowania obiektów), jest zorientowanym obiektowo językiem, przeznaczonym do definiowania klas. Drugi z nich, nazywany modelem E/R lub związków encji, jest notacją graficzną służącą do obrazowania organizacji bazy danych.
Ani język ODL, mimo że jest bardzo zbliżony do języka definicji danych obiektowych DBMS, ani model E/R nie mogą służyć bezpośrednio do definiowania struktury bazy danych. Zaklada się raczej, że projekt opisany w którejś z tych notacji zostanie przetłumaczony na język definicji danych konkretnego systemu DBMS. Ponieważ większość systemów baz danych operuje modelem relacyjnym, więc skoncentrujemy się na przekształcaniu zapisów w ODL i E/R właśnie do postaci relacyjnej.
Procesowi translacji i modelowi relacyjnemu poświęcamy rozdzial 3. Dalej, w podrozdziale 5.7 pokażemy, jak w sposób formalny opisywać relacyjne schematy baz danych w języku SQL.
W rozdziale 3 wprowadzimy Czytelników również w tajniki opisu związków, które dużą do formalnego zapisu zalożeń dotyczących związków między krotkami relacji. Związki umożliwiają oplacalną dekompozycję relacji, którą przeprowadza się w procesie nazywanym „normalizacją" relacji. Związki i normalizacja wyczerpują treść zawartą w podrozdziale 3.5 i następnych; zagadnienia te stanowią ważny element projektowania baz danych. Opisywane tematy są ważne zarówno dla tych, którzy bezpośrednio będą projektować relacyjne bazy danych, jak i dla tych, którzy będą musieli przekształcić projekt zapisany w ODL lub E/R do postaci relacyjnej i będą mieli problemy z projektem.
1.4.2. Programowanie
Rozdziały od 4 do 8 obejmują zagadnienia związane z programowaniem baz danych. Zaczniemy w rozdziale 4 od abstrakcyjnego potraktowania zapytań w modelu relacyjnym, polegającego na wprowadzeniu określonej na relacjach rodziny operatorów, która tworzy „algebrę relacji". Omówimy także tzw. „Datalog" - alternatywny sposób opisywania zapytań, w którym stosuje się wyrażenia logiczne.
1.5. PODSUMOWANIE 43
Rozdziaky od 5 do 7 są dedykowane programowaniu w języku SQL. Jak już wspominaliśmy, SQL jest obecnie dominującym językiem zapytań. W rozdziale 5 omówimy podstawy dotyczące zapytań w SQL oraz opisu schematu baz danych w tym języku. Prawie wszędzie w tych rozdziałach, oraz w następnych dwóch, posługujemy się standardem SQL, nazywanym SQL2. Jednak niektóre elementy charakterystyczne dla programowania w SQL znajdują się w komercyjnych wersjach tego narzędzia, ale nie ma ich w standardzie. W takich przypadkach korzystamy z późniejszego standardu, jeszcze nie całkiem formalnego, nazywanego SQL3.
Rozdział 6 obejmuje zagadnienia związane z wyzwalaczami i więzami danych w języku SQL. Ponieważ możliwości SQL2 w tym zakresie są dość ograniczone, więc poświęcamy trochę czasu rozwiązaniom proponowanym zarówno w SQL3, jak i w SQL2.
Rozdział 7 zawiera opis pewnych zaawansowanych aspektów programowania w języku SQL. Po pierwsze, podczas gdy najprostszy sposób programowania w SQL polega na korzystaniu z niezależnego interfejsu generującego zapytania, w praktyce większość kodu SQL jest osadzona w większych programach, zapisanych w językach konwencjonalnych, takich jak na przykład C. W rozdziale 7 nauczymy się, w jaki sposób dołączać kod zapisany w SQL do programu zewnętrznego oraz jak przekazywać dane między bazą danych a zmiennymi programu. Także w tym rozdziale znajdziemy sposoby opisywania w SQL transakcji, łączenia klienta z serwerem i autoryzowania dostępu do baz danych dla osób spoza grona właścicieli.
W rozdziale 8 zwrócimy uwagę na powstające właśnie zorientowane obiektowo standardy programowania baz danych. Wyróżnimy tutaj dwa kierunki. Pierwszy z nich, OQL (object query language) można traktować jako próbę dopasowania języka C++ do wymagań wysoko poziomowego programowania baz danych. W drugim, który sprowadza się do obiektowych elementów SQL3, można dopatrzyć się próby przekształcenia SQL i baz relacyjnych do postaci kompatybilnej z programowaniem zorientowanym obiektowo. Do pewnego stopnia oba te podejścia są podobne. Jednak jest wiele aspektów, które różnią je zasadniczo.
1.5. Podsumowanie
• Systemy zarzc~dzania bazami danych: (DBMS - database management systems): służą projektantom do strukturalizacji danych, użytkownikom do wyszukiwania i aktualizowania danych, a także ułatwiają organizowanie wielkich ilości danych oraz wielu jednoczesnych operacji na nich.
• Języki baz danych (database languages): Są to bądź języki, bądź ich składowe, przeznaczone do definiowania struktur danych (języki definiowania danych - data-definition languages) i do wyszukiwania
44
1. DZIEDZINA SYSTEMÓW BAZ DANYCH
i aktualizowania danych (języki operowania danymi - data-manipulation languages).
• Relacyjne systemy baz danych (relational database systems): Obecnie większość systemów baz danych realizuje model relacyjny, w którym podstawową strukturą danych jest tabela. W tych systemach najczęściej korzysta się z języka SQL.
• Zorientowane obiektowo systemy baz danych (object-oriented database systems): W niektórych wspólczesnych systemach baz danych można zaobserwować elementy zorientowanych obiektowo modeli danych, takie jak na przykład klasy, obszerne systemy typów, identyfikatory obiektów oraz dziedziczenie wlasności w podklasach. Prawdopodobnie w przyszłości większość systemów baz danych, nawet tych relacyjnych, będzie uwzględniać te wszystkie koncepcje.
• Pamięć drugiego i trzeciego poziomu (secondary and te~tiary storage): Wielkie bazy danych przechowuje się zazwyczaj na urządzeniach pamiętających drugiego poziomu, przeważnie na dyskach. Największe bazy danych potrzebują zastosowania urządzeń trzeciego poziomu, o pojemności o kilka rzędów większej niż dyski, ale także o kilka rzędów wolniejszych.
• Składowe systemu DBMS (components of a DBM,f): Wśród głównych składowych systemu zarządzania bazami danych należy wymienić: moduł obsługi zapytań, moduł zarządzania transakcjami oraz moduł zarządzania pamięcią.
• Moduł zarzctdzania pamięcią (storage manager): Moduł zarządzania pamięcią obsługuje zarówno pliki danych w pamięci drugiego poziomu, jak i bufory w pamięci operacyjnej, w których umieszcza się fragmenty plików. System zarządzania bazą danych utrzymuje indeksy, tzn. struktury danych zapewniające efektywny dostęp do danych.
• Moduł obsługi zapytań (query manager): Ważne zadanie modułu obsługi zapytań polega na „optymalizowaniu" zapytań, to znaczy znalezieniu właściwego algorytmu generującego odpowiedź na konkretne zapytanie.
• Moduł zarzctdzania transakcjami (transaction manager): transakcja stanowi podstawową jednostkę przetwarzania w bazach danych. Modul zarządzania transakcjami umożliwia jednoczesne przetwarzanie wielu transakcji, zapewniając także zachowanie właściwości ACID: niepodzielności, spójności, izolacji i trwalości.
• Systemy klient-serwer (client-server systems): Systemy zarządzania bazami danych zazwyczaj dzialają w architekturze klient-serwer, gkówna cześć bazy danych jest przetwarzana jako serwer, a po stronie klienta działa interfejs użytkownika.
• Aktywne elementy baz danych (active database elements): wspólczesne systemy baz danych zapewniają dostęp do pewnych postaci elementów aktywnych, zazwyczaj wyzwalaczy lub więzów integralności.
1.6. LITERATURA DO ROZDZIAŁU 1
45
1 Przyszłość systemów: Glówne trendy systemów baz danych obejmują takie zjawiska jak bardzo duże obiekty „multimedialne", na przykkad wideo lub obrazy oraz integrowanie danych z wielu oddzielnych źródeł w jednąbazę danych.
1.6. Literatura do rozdziaw 1
Istnieje wiele książek poświęconych ważnym aspektom implementacji systemów baz danych. W pozycjach [3] i [5] opisano implementację zarządzania transakcjami. Także tam, a również i w pozycji [7] przedstawiono opis implementacji rozproszonych baz danych. Natomiast implementację systemu plików omówiono w [11].
Pozycje [2], [4] oraz [6] dotyczą zorientowanych obiektowo systemów baz danych. Teorię baz danych przedstawiono w [1], [9] oraz [10]. W [8] można znaleźć wiele opracowań naukowych, które miały wpływ na kształtowanie się dziedziny baz danych.
1. Abiteboul S., Hull R., Vianu V.: Foundations of Databases. Addison-Wesley, Reading MA. 1995.
2. Bancilhon F., Delobel C., Kanellakis P.: Building an Object-Oriented Database System. Morgan-Kaufman, San Francisco, 1992.
3. Bernstein P.A., Hadzilacos V., Goodman N.: Concurrency Control and Recovery in Database Systen2s. Addison-Wesley, Reading, MA, 1987.
4. Cattell R.G.G.: Object Data Management. Addison-Wesley, Reading, MA, 1994.
5. Gray J.N., Reuter A.: Transaction Processing: Concepts and Technigues. Morgan-Kaufman, San Francisco, 1993.
6. Kim W. (ed.): Modern Database Systems: The Object Model, Interoperability and Beyond. ACM Press, New York, 1994.
7. Oszu M. T., Valduriez P.: Principles of Distributed Database Systems. Prentice Hall, Englewood Cliffs, NJ, 1991.
8. Stonebraker M. (ed.): Readings in Database Systems. Morgan-Kaufman, San Francisco, 1994.
9. Ullman J.D.: Principles in Database and Knowledge-Base Systems, t. I. Computer science Press, New York, 1988.
10. Ullman J.D.: Principles in Database and Knowledge-Base Systems, t. II. Computer science Press, New York, 1989.
11. Wiederhold G.: Database Design. McGraw-Hill, New York, 1983.