V tomto díle si napíšeme o tom, proč používat zrovna PostgreSQL, přiblížíme si význam architektury klient/server a především o uložení dat v databázi.
1.9.2004 10:00 | MaReK Olšavský | přečteno 29115×
PostgreSQL je relační databáze podporující SQL92 a SQL99 normu a dalších mnoho moderních rysů:
Server je vysoce rozšiřitelný (právě možnostmi definovat si vlastní datové typy a uložené procedury) a objektově-relační, což si také postupně ukážeme, co znamená.
Z hlediska programátora je výborná možnost přístupu z mnoha jazyků C/C++ (knihovny libpq a libpq++), scriptovacích jazyků Perl (pgsql_perl5), Python (PyGreSQL), ale i třeba javy (jdbc) a PHP. Ja sám jsem použil "pouze" poslední tři přístupy.
Vím, že teď asi vyvolám nevoli a možná i divoký flame pod článkem, ale PostgreSQL se mi jeví jako mnohem lepší databázové řešení, než MySQL, protože nabízí spoustu vymožeností, které MySQL nezvládá. Naproti tomu MySQL je nepoměrně rychlejší a na menší webové aplikace více, než dostatečná. Jako vždy je to především o aplikaci.
Databáze slouží k uložení nejrůznějších dat, jak už plyne z názvu. Data mohou být nejrůznějších typů od čísel celých, nebo v plovoucí desetinné čárce, přes textové údaje, datumy, časy až po binární soubory (o vkládání těch si myslím něco velmi nepěkného, ale jde to). Data představují vlastně vše, co chceme, aby nám databáze umožnila nějakým způsobem uložit a potom dále zpracovávat.
Databáze (ne databázový server) si můžeme představit jako sešit, kde se ukládají datové tabulky jako listy. V datových tabulkách pak najdeme rádky dat, které se v korektní terminologii nazývají větami. Každý záznam do databéze je jednou větou a skládá se z jedné (to by asi moc význam nemělo), nebo více položek. Každá z položek má určený svůj typ a tím ve většině případů i určenou velikost v bytech.
Pojem a koncepce relační databáze vychází s toho z matematického modelu, kdy můžeme pospojovat data z různých tabulek pomocí tak zvaných relačních operátorů (tj. =, <, > a jejich kombinací). Výsledkem je kartézký součin spojených množin.
Relace nám umožňují data rozebrat (tento proces se také nazývá normalizace databáze) do několika tabulek, v tomto díle si ukážeme logiku, jak data rozdělit. Rozdělení dat nám umožní jednako redukovat velikost databáze, obsazeného prostoru, odstraněním redundantních dat a druhako nám urychlí manipulaci, zejména pak vyhledávání, s daty, protože to je podstatně rychlejší v tabulkách, kde jsou kratší věty, než dlouhé. Na druhou stranu rozdělení dat do několika vět zvyšuje nároky kladené na programátora, protože je třeba uhlídat konzistenci dat. K tomu nám mohou pomoci i triggery (spouště).
Relace jsou několika typů, je to relace 1:1, tzn. jednomu záznamu v tabulce t1 odpovídá (přes nějaký spojovací klíč) právě jeden záznam v tabulce t2, relace 1:n, kdy jednomu záznamu v t1 odpovídá (zase přes klíč) n z tabulky t2 a nakonec m:n, která se v praxi nepoužívá, protože je i neefektivně matematicky popsatelná a převádí se na relace m:1 a 1:n přes rozumnou spojovací tabulku. Příkladem na poslední relaci může být vztah kategorie_výrobků <-> výrobky, kdy jeden výrobek může patřit do více kategorií a je obvyklé, že jedna kategorie obsahuje více výrobků.
V dobách prvních PC byly aplikace ve většině případů jednovrstvé, to znamená, že aplikace řešila nejen nějaké zpracování dat, ale i jejich lokální uložení. Většina firem si nemohla dovolit zasíťovat kanceláře a vybudovat nějaký centrální databázový, nebo aplikační server. I dnes můžeme najít řadu aplikací, která takto pracují. Jsou to například kancelářské aplikace, či většina her.
Pro práci s většími a velkými objemy dat je vhodné přejít alespoň na dvouvrstvou architekturu, která může v případě potřeby běžet i lokálně na jednom počítači. Vy v aplikaci zpracováváte data, vyhodnocujete data, ale o jejich ukládání a případné filtrování, třídění, když o nějaká požádáte se stará SQL server. Troufám si tvrdit, že lokálně spuštěný server, na stejném počítači, kde pracuje, má tak 90-95% lidí, kteří věnují wwwývoji.
Při této architektuře máte v optimálním případě oddělenu práci s daty od jejich uskladnění a komunikaci mezi klientskou aplikací a serverem řešíte pomocí nějakého standardního rozhraní, kterým může být třeba SQL jazyk.
Při práci na velkých projektech, jaké se dělají třeba pro banky, vstupuje do hry třívrstvá architekrura, kdy mezi klientskou část (která se pak skoro už redukuje jen na formuláře pro zadávání a čtení dat) a databázový server vstupuje nějaký aplikační server, kde běží tzv. middleware. Poslední dobou se střetávám s tím, že mnoho takovýchto věcí běží na J2EE, takže se používají servery, nebo rozšíření serverů, Tomcat, JBoss, BEA Weblogic, případně nastupuje architektura .NET. Třeba Java běžící na 64 bitovém systému přímo od SUN Microsystem je rychlý a robusní nástroj na řešení této vrstvy aplikace.
Nyní si už pomůžeme modelovým příkladem databáze, na které si ukážeme, jak se prakticky provádí normalizace (tj. základní metoda optimalizace). Použijeme fiktivní obchod. O každé objednávce potřebujeme vědět, komu patří, co obsahuje za produkty a případně i dodavatele těchto produktů. Takže tabulka by mohla obsahovat tyto položky: ID_položky, jméno_zákazníka, příjmení_zákazníka, Adresa, Název_produktu, Popis,Cena, Číslo_objednávky, Datum, Množství, Dodavatel. Pro každou položku objedbávky by přibyla do databáze jedna věta, takže by objem dat rostl díky redundanci (nadbytečnosti) nade všechny meze.
Normalizace je sada pravidel, jak byste měli postupovat při návrhu struktury databázových tabulek. Je 7 stupňů normalizace, protože v praxi se používají v podstatě jen první 3 stupně, povíme si jen o nich. Jednolivé stupně jsou:
První stupeň nám říká, že bychom měli rozdělit data tak, aby opakující se sloupce byly v nových tabulkách pouze jednou a zároveň musí být mezi těmito sloupci definovatelná relace, obvykle přes nějaký číselný indikátor. Takže v našem případě by se tabulka rozpadla na tabulky:
Zákazníci: ID_zákazníka, Jméno, Příjmení, Adresa, Číslo_objednávky, Datum, ID_Zboží, Množství, Dodavatel
Produkty: ID_Produktu, Název, Popis, Cena, Obrázek.
Každý asi hned na první pohled vidí, že to také není ještě to pravé ořechové, protože když při objednávání zboží se nám budou neustále opakovat v databázi všechna data o zákazníkovi. Každopádně už zde nastane jistá úspora místa a nárůst rychlosti vůči původnímu stavu.
Druhý stupeň znamená že rozdělíme opakující se sloupce, které vytvářejí částečné závislosti do nových tabulek. V našem případě je logické, že každá tabulka obsahuje několik položek, takže je logické, kdy oddělíme tabulku objednávky od tabulky zákazníků, takže budeme mít následující tabulky:
Zákazníci: ID_Zákazníka, Jméno, Příjmení, Adresa
Produkty: ID_Produktu, Název, Cena
Objednávky: ID_Objednávky, ID_Zákazníka, Datum, Množství, Dodavatel
Objednávky_kompletace: ID_Komletace, ID_Zákazníka, ID_Objednávky
Přičemž poslední tabulka tvoří nějaké logické propojení mezi zbývajícími dvěmi.
V tomto stupni ještě stále není optimalizace na vysoké úrovni, opakuje se nám třeba název dodavatele v každé položce objednávky.
Vyžaduje důsledné odstranění dat, které nejsou v přímém vztahu s objektem. Tento krok bude v té mojí ukázkové databázi asi nejrazantnější. Při tomto třetím kroku optimalizace našeho příkladu se dostaneme k následující struktuře databáze:
Zákazníci: ID_Zákazníka, Jméno, Příjmení, Adresa
Produkty: ID_Produktu, Název, Popis, Cena
Dodavatelé: ID_Dodavatele, Jméno
Objednávky: ID_Objednávky, ID_Zákazníka, Datum
Objednávka_položka: ID_Položky, ID_Objednávky, ID_Produktu, Počet, ID_Dodavatele
Jednotlivé položky jsou v databázi pojmenované logicky, takže jsou, snad, vidět klíče, neboli položky, přes které jsou tabulky provázané.
Další stupně si dovolím jen vyjmenovat, protože z jejich názvu a z pozdějších kapitol vyplyne k čemu slouží, ale tyto stupně většinou zůstávají už jen na teoretické úrovni.
Databázi jsem ale nenormalizoval příliž optimálně, do tabulky položek objednávky by bylo slušné přidat cenu zboží v momentě objednávky, protože pokud se tato změní (obvykle zvýší), je slušné dodat zákazníkovi zboží za cenu v době objednání, pokud cenu obchodník sníží, je asi slušné mít obchod ošetřený tak, abyste dodávali zboží za novou cenu, ale to už je na uvážení programátora i obchodníka.
Vždy normalizujte databázi, jak moc a jakým způsobem bude nakonec vždy záležet pouze na Vašem vhledu a na zkušenostech, které už jste při programování získali.
Hlavním důvodem pro optimalizaci je úspora místa na disku a tím i zrychlení databáze, protože server nebude muset procházet tabulky, které mu narůstají takřka nekonečnou rychlostí. Zkuste si cvičně započítat, kolik místa ušetříte, když budete mít třeba 15.000 objednávek, pro popisy a názvy položek vyhradíte 100 bytů, ale ID_xxx budou mít bytů 10. Při třeba 100.000 objednávek, z nichž každá bude mít průměrně 3 položky už dojdete na velmi zajímavá čísla.
Normalizace dat přináší také jednu menší nevýhodu. Programátor musí více hlídat integritu dat. Při zrušení nějakého produktu jej tedy nejen smazat v tabulce produktů, ale informovat i všechny, kdo ten produkt měli objednaný a smazat všechny položky objednávek, kde byl tento produkt, nebo jej jinak zneplatnit. V té původní verzi stačilo jen informovat zákazníky a smazat věty, které jej obsahovaly. K tomuto mu mohou pomoci triggery (spouště) a uložené (stored) procedury, kteréžto nástroje PostgreSQL umí velmi dobře.
Dneska jsem napsal o důvodech, proč používat (proč mám rád PostgreSQL), něco o tom, proč databáze nazýváme relačními a něco malinko o optimalizaci tabulek pro ukládání dat. Příští díl bude o vytvoření databáze, uživatele a možná i o instalaci PostgreSQL serveru na LINUXu.