Każdy średnio doświadczony programista wie, że redundancja danych w bazie jest zła, nie powinna występować i w ogóle powinno się za nią obcinać ręce. W tym bardzo krótkim i pobieżnym tekście postaram się udowodnić, że istnieją przypadki, w których daje ona pewne korzyści.
W dużym skrócie redundancja to (zbędne) powielenie danych. W teorii relacyjnych baz danych istnieje przekonanie, iż dane powinny być ze sobą powiązane relacjami w taki sposób, aby nie występowała ich nadmiarowość. Jest to przekonanie nadwyraz słuszne, ponieważ takie podejście ogranicza (a często wręcz uniemożliwia) powstawanie niespójności danych w bazie.
Projektanci i programiści, którzy wyrośli tylko i wyłącznie na teorii, często starają się wymusić relacyjność danych w sposób graniczący wręcz z absurdem. Widziałem już tabele w bazie, które posiadają tylko dwa pola (oba - o zgrozo - będące częścią PRIMARY KEY) i służą do łączenia danych z innych tabel w relacji 1:1 (dla niezorientowanych - jest to po prostu głupie). Nie ma to wprawdzie nic wspólnego z redundancją, ale napisałem to gwoli wstępu. ;)
Jako modelowy przykład, na którym będę prezentował wady i zalety redundancji, wybrałem sklep internetowy. Zakładam, że składa się on (między innymi) z następujących tabel:
- klienci - dane adresowe klientów sklepu,
- towary - dane towarów, które oferuje dany sklep,
- ceny - ceny oferowanych towarów (są przechowywane osobno, ponieważ mogą się zmieniać w czasie),
- zamówienia - dane dotyczące zamówień towarów przez klientów,
- szczegóły zamówień - lista pozycji składających się na dane zamówienie.
Jak do tej pory wszystko jest chyba jasne. Towary mają przypisane ceny (w relacji 1:n), które to z kolei mają określony okres obowiązywania (w postaci zakresu dat od-do). Zamówienia mają przypisaną listę składających się na nie towarów (również w relacji 1:n).
Na proces składania zamówienia przez klienta złoży się więc dokonanie wpisu w tabeli zamówień, który to wpis zawierać będzie m. in. identyfikator klienta i datę złożenia zamówienia, oraz dokonanie wpisów w tabeli szczegółów zamówień (jeden rekord na każdy zamawiany towar). Jeśli chcemy uniknąć redundancji danych, to oczywiście do tabeli zamówień wpiszemy jedynie identyfikator klienta, a do tabeli szczegółów - identyfikatory towarów (datę sprzedaży potrzebną do sprawdzenia ceny towarów w momencie składania zamówienia mamy w tabeli zamówień).
Zobaczmy teraz, jaką ścieżkę trzeba przejść aby wyciągnąć kompletne dane zamówienia.
Z tabeli zamówień wyciągamy identyfikator klienta, na podstawie którego wyciągamy jego dane z tabeli klientów. Teraz wyciągamy z tabeli szczegółów zamówień listę zamówionych towarów, dobierając jednocześnie ich cenę na podstawie identyfikatora towaru i daty zamówienia (ceny - jak już napisałem wcześniej - zmieniają się w czasie).
W tym wypadku można pokusić się o małą redundancję danych. Do tabeli zamówień można dodać kompletne dane adresowe klienta. Rozwiązanie takie ma dwie zalety: po pierwsze - zmniejsza ilość koniecznych do wykonania zapytań (akurat w tym wypadku ma to niewielkie znaczenie), a po drugie - pozwala na przechowywanie danych adresowych klienta z momentu składania zamówienia (klient może w końcu zmienić swoje dane adresowe, a taka zmiana będzie - co oczywiste - zapisana w tabeli klientów). Jak więc widać, redundancja jest w tym przypadku uzasadniona zarówno w kontekście wydajności, jak i funkcjonalności systemu. Oczywiście, można stworzyć dodatkową tabelę z danymi adresowymi klientów (z zakresem dat, w jakim obowiązują), ale to tworzy dodatkowe komplikacje, często niepotrzebne (szczególnie w małej skali).
Przyjrzyjmy się teraz cenom. Do tabeli z listą zamówionych towarów można dołożyć cenę obowiązującą w momencie składania zamówienia. Nie da nam to żadnych korzyści funkcjonalnych (cenę tą możemy bez problemu wyciągnąć na podstawie identyfikatora towaru i daty zamówienia), ale upraszcza działanie systemu i zwiększa jego wydajność. Ryzyko wystąpienia niespójności danych w tym przypadku praktycznie nie istnieje - dane zamówienia nie są w końcu modyfikowane wstecz.
Na powyższym przykładzie bez trudu można zauważyć pewne korzyści płynące z redundancji danych. Prawda jest jednak taka, że uwidaczniają się one tylko i wyłącznie w systemach o naprawdę dużej skali. Przy projektowaniu niewielkich lub przeznaczonych dla małej ilości użytkowników systemów lepiej pozostać przy “standardowej” teorii relacyjnych baz danych, niż ryzykować niespójność. Redundancję mogą usprawiedliwiać jedynie względy wydajnościowe, a i w takim wypadku należy bardzo dogłębnie przeanalizować ryzyko jej wprowadzenia. Nadmiarowość danych usprawiedliwiona jedynie uproszczeniem działania systemu może w bardzo krótkim czasie zemścić się na twórcy.
Przykład sklepu internetowego został przeze mnie wybrany nie bez powodu - każdy chyba zna zasadę jego działania. Nie jest to jednak dobry przykład - istnieją w końcu systemy, w których dojście do jakichś danych wymaga wykonania kilkunastu czy nawet kilkudziesięciu zapytań. Redundancja może okazać się w takich systemach bardzo przydatna.
Wszystkie osoby czytające ten tekst proszę o odrobinę dystansu i zastanowienia przed próbą wdrożenia moich rad. Przede wszystkim zastanówcie się, czy Wasz system potrzebuje w ogóle redundancji ze względu na wydajność. Być może da się zoptymalizować go w inny, mniej ryzykowny sposób.
kategorie: Developement
[ komentarze: 3 ]
listopad 27th, 2006 at 10:42
Ciekawe podejscie, ale w ten sposob przeczy to troche calej ideii bazy :) Wiem, ze chodzi o zwiekszenie wydajnosci, ale to mozna uzyskac poprzez cachowanie zapytan. Ja jestem za bardziej konwencjonalnymi metodami, jezeli serwer sie nie wyrabia to trzeba cos zmienic, ale nie w taki sposob jaki Ty proponujesz w bazie :)
pozdrawiam.
listopad 27th, 2006 at 11:51
Cachowanie danych nie sprawdza się w np. systemach/aplikacjach czasu rzeczywistego.
Celowo napisałem, aby zawsze zastanowić się nad tym, czy nie da się aplikacji zoptymalizować w innych sposób (np. przez zastosowanie wspomnianego przez Ciebie systemu cache - który notabene nie zawsze daje dobre rezultaty). Gdybym pisał tekst o optymalizacji aplikacji pod kątem wydajności, to nie wspomniałbym ani słowem o stosowaniu redundancji. Napisałem jednak tekst poświęcony właśnie redundancji - nie można go traktować jako źródła wiedzy dotyczącej optymalizacji. ;)
listopad 28th, 2006 at 13:45
W sumie masz racje :) Zawsze trzeba wszystko jakoś tak sprytnie wypośrodkować. Bo przecież można zrobić super aplikacje bazodanową gdzie dane nie będą się powtarzały, baza będzie ekstra spójna, ale będzie mało wydajna. A można zrobić bazę taką maksymalnie prostą, ale zajmującą giga niepotrzebnych danych, ale za to super szybką ;) Jak widać wszystko jest względne, i zależy od punktu widzenia, i potrzeby w danej chwili. Niemniej jednak tekst ciekawy, a ja teraz czekam na tekst o optymalizacji :D