UNICORN COLLEGE Katedra informačních technologií BAKALÁŘSKÁ PRÁCE Nástroj na tvorbu testovacích dat pro zátěžové testování DB Autor BP: Marek Chytrý Vedoucí BP: Ing. Miroslav Žďárský 2012 Praha
ČESTNÉ PROHLÁŠENÍ Prohlašuji, že svoji bakalářskou práci na téma Nástroj na tvorbu testovacích dat pro zátěžové testování DB jsem vypracoval samostatně pod vedením vedoucího bakalářské práce a s použitím odborné literatury a dalších informačních zdrojů, které jsou v práci citovány a jsou též uvedeny v seznamu literatury a použitých zdrojů. Jako autor uvedené bakalářské práce dále prohlašuji, že v souvislosti s vytvořením této bakalářské práce jsem neporušil autorská práva třetích osob, zejména jsem nezasáhl nedovoleným způsobem do cizích autorských práv osobnostních, a jsem si plně vědom následků porušení ustanovení 11 a následujících autorského zákona č. 121/2000 Sb. V... dne.... Marek Chytrý
PODĚKOVÁNÍ Děkuji vedoucímu bakalářské práce Ing. Miroslavovi Žďárskému za účinnou metodickou, pedagogickou a odbornou pomoc a další cenné rady při zpracování mé bakalářské práce. Dále chci velmi poděkovat své manželce za oporu, povzbuzení a vytvoření příjemného prostředí pro psaní této práce a rodičům, kteří mi umožnili studium a během studia mě podporovali.
NÁSTROJ NA TVORBU TESTOVACÍCH DAT PRO ZÁTĚŽOVÉ TESTOVÁNÍ DB TOOL FOR CREATING TEST DATA FOR STRESS TESTING DB 6
ABSTRAKT Cílem této bakalářské práce je navrhnout nástroj na generování dat pro zátěžové testy. Aby však tato data byla správně navržena, je potřeba porozumět databázi, jak s daty pracuje a jak můžeme na základě správného návrhu datové struktury tuto činnost co nejvíce zefektivnit. V teoretické části jsou nejprve popsány základní pojmy týkající se relačních databází a zátěžových testů. Poté jsou vysvětleny způsoby sběru dat z databáze a je ukázáno na příkladech, jak lze příkazy pro práci s daty optimalizovat. Dále navazují informace o způsobech, jak databáze volí co nejefektivnější přístup k datům za použití optimizeru a statistik databáze. Práce se nezaměřuje na konfiguraci databáze nebo hardwaru serveru, to by bylo už na téma jiné bakalářské práce. V praktické části je pak popsán navržený nástroj pro generování dat, jeho části a princip generování testovacích dat. Zde je také základně popsán jazyk PL/SQL, ve kterém je nástroj naprogramován. Klíčová slova: relační databáze, zátěžové testy, optimalizace, optimizer, statistiky, PL/SQL 7
ABSTRACT IN ENGLISH The aim of this bachelor thesis is to propose a tool for generating data for stress tests. In order that this data are properly designed, it is necessary to understand the database; how it works with data and how we can do this activity more effective based on right proposal of the data structure. In the theoretical section there are firstly described the basic concepts related to relational databases and stress tests. Then there is explained how we collect data from the database and shown in the examples, how the commands for working with data can be optimized. Further follow information how database chooses the most effective access to data using the optimizer and statistics database. The thesis does not focus on the configuration database or hardware; it would be the topic of another thesis. In the practical part there is described a tool designed to generate data, its parts and principles of generating test data. There is also described programming language PL/SQL, in which the tool is programmed. Keywords: relational database, stress tests, optimization, optimizer, statistics, PL/SQL 8
OBSAH 1. Úvod... 11 2. Základní pojmy k optimalizaci databází... 12 2.1. Databáze... 12 2.1.1. Relační databáze... 13 2.2. Zátěžové testy... 15 2.2.1. Získání dat pro testování... 15 2.3. Přístupy k tabulkám... 16 2.3.1. Exekuční plán... 18 2.4. Indexy... 21 2.4.1. B-Tree index... 21 2.4.2. Bitmap index... 26 2.4.3. Indexově organizované tabulky (Index organized tables)... 28 2.5. Optimizer... 30 2.5.1. RBO (Rule based optimizer) a CBO (Cost based optimizer)... 31 2.6. Statistiky... 34 2.7. Hinty... 37 3. Praktická část - Generování dat... 38 3.1. Úvod do PL/SQL... 38 3.1.1. Balíčky, procedury a funkce... 38 3.2. Princip generování... 43 3.2.1. Zdrojová data pro generování... 43 3.2.2. Balíček fetch_source_data... 44 9
3.2.3. Balíček random_data... 46 3.2.4. Balíček package_random_table... 47 3.2.5. Pomocné balíčky, funkce a procedury... 50 3.3. Export a import velkého množství dat v databázích Oracle... 51 4. Závěr... 52 5. Conclusion... 54 6. Zdroje... 56 7. Seznam použitých symbolů a zkratek... 58 8. Seznam obrázků... 59 9. Seznam tabulek... 60 10
1. ÚVOD Vysoká dostupnost a krátká doba odezvy informačních systémů se v dnešním světě pokládá za samozřejmost. Pokud se některá internetová stránka nenačte uživateli do tří sekund, uživatel pravděpodobně takovou stránku po této době zavře a podívá se raději po jiné stránce, která se mu dostatečně rychle zobrazí. Tím narážím na fakt, že jedním z nejdůležitějších vlastností informačního systému je jeho doba odezvy. Tato oblast informačních technologií není jednoduchá, ale dnešní uživatelé předpokládají a berou za samozřejmost, že vše probíhá bez velkých prodlev. Navíc požadavky na zajištění dostupností a zvládání zátěží v době špiček je stanoveno v provozních smlouvách a dokumentech SLA. Pokud jsou tato stanovení porušena, pak provozovatel informačního systému neplatí jen ušlé zisky společností, ale může navíc platit i smluvní pokuty. Na základě požadavků zajištění nabízeného výkonu je potřeba tyto vlastnosti systému otestovat. A testování je zapotřebí nejen při zavádění systému, ale třeba i při změně softwarových technologií. Podle internetového článku na System On-line (1) je zapotřebí testovat výkon v těchto případech: 1. Výkonnostní akceptace řešení 2. Optimalizace hardwarové a softwarové konfigurace 3. Zjištění limitů hardwarové a softwarové konfigurace 4. Hledání úzkých míst již provozovaného systému 5. Ověření výkonnosti po změně technologie, po upgradu na novou verzi (operačního systému, aplikace, databáze) nebo po významné změně konfigurace Na vysoké škole Unicorn College jsem se seznámil s databází Oracle a její administrací a také se způsoby testování. V obou kurzech byly probírány zátěžové testy, ale v každém z jiného úhlu pohledu. Velmi mě zaujala myšlenka spojit informace z těchto kurzů a vytvořit praktický nástroj pro generování dat pro zátěžové testy pro relační databáze. 11
2. ZÁKLADNÍ POJMY K OPTIMALIZACI DATABÁZÍ 2.1. Databáze Data v jakémkoliv systému je potřeba perzistentně ukládat, aby bylo možné je později kdykoliv opět načíst. V jednoduchých systémech nebo domácích počítačích stačí na uložení dat souborový systém, pokud ale potřebujete například: 1. Data rychle vyhledávat 2. Přistupovat k datům paralelně 3. Pokročilejší nastavení práv přístupů k datům 4. Vybírat pouze část z celé množiny dat podle potřeb uživatele systému. V těchto případech souborový systém určitě nedostačuje a je potřeba použít databázi. Databáze slouží primárně k trvalému uložení dat. Dnes navíc poskytuje mnohé pokročilé funkce. Například zabezpečení, zálohování, archivaci, analýzu dat a další. Databáze je jedna z nejdůležitějších částí systému, a proto je potřeba tuto oblast nezanedbat. Většina lidí si pod pojmem databáze představí tabulky dat, ale toto je jen jeden typ databází, relační databáze. Tyto databáze jsou tvořeny relacemi. Tento typ databází ale není jediný, můžeme se ještě setkat s databázemi objektovými nebo objektově-relačními. Objektové databáze neukládají data do tabulek, ale přímo ukládají objekty, se kterými programátor pracuje. Proto pro programátory je mnohem intuitivnější ukládat jednotlivé objekty tříd a nemuset se zamýšlet nad tabulkami nebo převodem objektů do tabulek. Dalším možným přístupem je objektově-relační databáze. V tomto případě je potřeba nejprve nadefinovat způsob převodu mezi tabulkami a objekty. Aby bylo možné objekty do tabulky namapovat, zakládají se sloupce tabulky tak, aby odpovídaly jednotlivým atributům třídy, jejichž objekty se mají do tabulky vložit. Jednotlivé řádky tabulky pak odpovídají jednotlivým objektům. Tento přístup se snaží brát přednosti z obou zvýše zmíněných typů databází, ale v konečném důsledku je náročné samotné mapování tabulek na objekty a navíc musí programátoři popsat daný způsob mapování tabulek na objekty. 12
Tato práce se zaměřuje pouze na relační databáze, a konkrétně na relační databázi Oracle. Pro tuto databázi jsou napsány i praktické příklady, a je pro ni primárně určen i nástroj pro generování dat. Tato databáze je zároveň nejrozšířenější na trhu komerčních databází a je používána pro rozsáhlé informační systémy. V této bakalářské práci je pojem databáze používán dvěma různými způsoby, a to jednak tento pojem je používán ve smyslu trvalého úložiště dat a jednak ve smyslu komplexního systému, který se stará o kompletní správu všech dat uložených v daném systému. 2.1.1. Relační databáze Základem relačních databází jsou tabulky. Tabulky se skládají ze sloupců, ve kterých jsou uloženy jednotlivé proměnné. Jednotlivé sloupce jsou pojmenovány tak, aby co nejlépe vystihly data v nich uložená. Pro každý sloupec je definovaný datový typ, ten určuje, jaká data jsou ve sloupci uložena. V jednotlivých řádcích jsou pak uložené jednotlivé záznamy. Pro přístup k těmto datům se používá speciálně navržený jazyk SQL. Ten se skládá z několika skupin příkazů, které jsou rozděleny podle svého účelu. Skupiny příkazů: 1. DDL (Data Definition Language) příkazy pro definice struktur databáze tabulky, indexy 2. DML (Data Manipulation Language) příkazy pro manipulaci s daty 3. DCL (Data Control Language) příkazy pro řízení kontroly nad daty 4. TCC (Transaction Control Commands) příkazy pro řízení transakcí O každé skupině jazyků by mohly být napsány celé knížky. V této práci jsou jednotlivé typy příkazů popsány pouze z pohledu optimalizace. V teoretické části práce je popisována skupina příkazů DML. Hlavně je zde popsáno, jakým způsobem databáze vrací řádky pro příkaz SELECT. Naváže kapitola o indexech, které slouží ke zrychlení přístupu do tabulek databáze a spadají do skupiny příkazů DDL. V praktické části pak bude popsáno, jakým způsobem budou generována data pro zátěžové testy a také jak jsou vkládána do tabulek. Data v tabulkách mohou mít velmi složitou strukturu a množství dat může být velmi rozsáhlé. Pro správné navržení datových struktur je potřeba zohlednit mnoho faktorů a po samotném navržení databázového systému je následně potřeba ověřit splnění požadavků. 13
Není možné zjistit až na produkci, že byla nevhodně navržena. Proto je maximálně důležité ještě před jejím zavedením do ostrého provozu ověřit pomocí zátěžových testů, že je schopna zpracovávat požadované objemy dat. (2) 14
2.2. Zátěžové testy Testování výkonu a možné zátěže databázového systému není jednoduchá činnost a často je zanedbávána nebo nedoceňovaná. Při práci na projektu pak bývá tato oblast nejčastěji ta, která je z nedostatku buď finančních, nebo časových zdrojů zrušena. Přesto pokud je požadován kvalitní software, má tato oblast nezastupitelné místo. Univerzální návod na testování obecně bohužel neexistuje a myslet si, že informační systémy neobsahují žádné chyby, také není správný přístup. Pokud jsme tedy již rozhodnuti, že chceme testovat, musíme se následně zamyslet, co konkrétně a jakým způsobem bude testováno. Rozhodně je také důležité vědět, z jakého důvodu budou testy prováděny. Tato bakalářská práce se zaměřuje na testování výkonu a zjišťování jeho možné optimalizace. Na základě znalostí o optimalizaci je pak navržen způsob efektivního generování dat. Aby data byla věrná datům produkčním, je potřeba je vygenerovat správným způsobem. Generování dat není jediným způsobem, jak získat data pro databázi. Data je možno získat i jinými způsoby. 2.2.1. Získání dat pro testování Jednou z možností je přímo použít produkční data. Tato varianta je někdy používána, ale není úplně vhodná kvůli velkému riziku zneužití těchto dat a musí být v takovém případě přijata zvýšená bezpečnostní opatření. Toto řešení bývá voleno z důvodu jednoduchosti a rychlosti získání dat, v případech kdy potřebujeme dodat data do druhého dne. Dalším řešením je data masking. Tímto způsobem se použijí data produkční, ale znehodnocená tak, aby nebylo možné je obnovit, ale struktura těchto dat je shodná s produkčními daty. Viz obrázek 1. Jinou možností je vlastní vygenerování dat. Takto získaná data musí odpovídat datům reálným, co se týká hodnot a pravděpodobnosti opakování. Proto jsou tato data také nejvěrnější datům reálným a navíc zde nejsou bezpečnostní rizika jako u použití produkčních dat. Jinou obrovskou výhodou těchto dat je jejich volitelný rozsah. Pokud je dat nedostatek, není problém je doplnit. Toto u předešlých způsobů není možné. Na druhou stranu někdy může být obtížné získat pravděpodobnostní rozdělení dat, abychom dokázali 15
vygenerovat totožná data. Pokud ale tyto pravděpodobnosti rozdělení máme, získáváme další nezanedbatelnou výhodu, a tou je možnost vygenerovat libovolně velkou množinu dat, která odpovídá produkčním datům, a tímto způsobem se můžeme připravit na budoucí zátěž databáze. Obrázek 1: Data masking LAST_NAME SSN SALARY AGUILAR BENSON D SOUSA FI ORANO 203-33-3234 323-22-2943 989-22-2403 093-44-3823 40 000 60 000 80 000 45 000 LAST_NAME BIRTH_NUMBER SALARY ANSKELFD BEPFZPEFZ ERPVQAJEF GEPV 111-23-1111 111-34-1345 111-97-2749 111-49-3849 40 000 60 000 80 000 45 000 Zdroj: Vlastní zpracování Visio 2007 podle zdroje: http://datamart.org/2010/03/12/data-masking/ Před popisem samotného generování bude popsán způsob zpracování příkazu od uživatele přes možné teoretické optimalizace dat v databázi. 2.3. Přístupy k tabulkám Přístupy do tabulky jsou způsoby, jakými jsou procházeny tabulky a vracena uživateli požadovaná data. Aby bylo možné vykonávat zátěžové testy, je nutné porozumět, jak databáze vyhledává žádaná data. Přístupů máme několik: Full table scan (FTS) Tento přístup načítá najednou všechna data z tabulek a následně je prochází, zda odpovídají zadaným kritériím. Což samozřejmě představuje náročné I/O operace. Databáze naštěstí data nenačítá postupně ale po blocích, čímž je načítání podstatně zrychleno. Tento přístup je zvolen, pokud má být načtena například celá tabulka nebo pokud máme malé množství dat a je rychlejší je všechna načíst, než je procházet pomocí některého z indexů. 16
Pokud nebyly nad sloupci tabulky vytvořeny indexy, nebo byly vytvořeny špatným způsobem, pak se také použije tento přístup. Rowid access Tento přístup je použit v případě, když jsou rowid řádky známy a databáze načte konkrétní blok, kde se požadovaná řádka přímo nachází. Proto se nejprve zjišťují rowid řádek pomocí full table scanu nebo index scanu. Pokud by byly známy rowid řádky, mohla by být zadána v podmínce where rowid = <řádek>, a poté by nebylo zapotřebí žádné další prohledávání tabulek, ale to většinou není známo. Příklad příkazu, kdy je známa rowid a databáze může k řádku přistoupit přímo přes ně: SELECT * FROM emp WHERE rowid = chartorowid('aaaw07aaeaaaai7aaa'); V příkazu byla použita funkce chartorowid, která slouží k převodu řetězce na rowid. Index unique scan (IUS) Index unique scan je přístup, který využívá indexů (indexy budou popsány v samostatné kapitole). Tyto indexy jsou vloženy k některému ze sloupců tabulky a databáze v nich vyhledává v případech, kdy je nad tímto sloupcem WHERE podmínka a vyfiltrovaných dat je malý počet. Index range scan (IRS) Tento přístup je podobný přístupu index unique scan, jen podle indexů nevyhledáváme unikátní hodnoty, ale interval hodnot. Většinou v podmínce příkazu jsou horní a dolní omezení pro vyhledávání v indexech. Fast full index scan (FFIS) Přístup je zvolen v případě, kdy hledaná hodnota je zároveň uložena v indexu a není potřeba přistupovat do samotné tabulky. FFIS je velmi podobný přístupu Full table scan. Stejně jako FTS načítá více bloků naráz, čímž urychluje získávání dat. Pokud je zvolen tento přístup, odpadá druhá fáze rowid access, která se ve všech ostatních případech vždy vykonává. 17
Databáze, pokud přistupuje k datům, jen zřídka volí pouze jeden přístup. Většinou volí kombinaci dvou a více přístupů. Které přístupy a v jakém pořadí jsou zvoleny, vyhodnocuje optimizer. Toto vyhodnocování je prováděno pro každý DML příkaz. Popis přístupů a dalšího zpracování dat, které jsou vracena databází, popisuje podrobně exekuční plán. 2.3.1. Exekuční plán Tento plán se skládá z několika kroků a tyto kroky neobsahují pouhý výběr dat. Může se jednat i o filtrování, řazení, seskupování nebo spojování tabulek. Exekuční plán je velmi důležitý, abychom dokázali pochopit navržené postupy optimizerem a mohli identifikovat případná slabá místa. Možné akce vykonávané v databázi a zobrazené v exekučním plánu: Pořadí tabulek vzájemně odkazovaných, ze kterých mají být data načtena. Způsob spojení tabulek, na základě příkazu uživatele. Operace s daty, například filtrování, řazení nebo seskupování. Exekuční plán je prováděn pro příkazy INSERT, DELETE, UPDATE nebo SELECT. (3) Pro zobrazení exekučního plánu stačí před příkaz napsat EXPLAIN PLAN FOR. Například: EXPLAIN PLAN FOR SELECT last_name FROM customers; Na následujícím obrázku je příklad exekučního plánu: Obrázek 2: Exekuční plán Zdroj: Vlastní zpracování 18
Z uvedeného exekučního plánu vyplývají následující operace. V prvním kroku jsou tabulky spojeny, pak je použit full table access a z těchto dat je vybrána pouze určitá data podle indexu ORD_CUSTOMER_IX. V následující tabulce je uvedeno, jaké sloupce může exekuční plán obsahovat a jejich stručný význam. Tabulka 1: Popis sloupců exekučního plánu Sloupeček exekučního plánu Operation Popis Prováděná operace v rámci jednoho kroku exekučního plánu. Name Rows Bytes Odkaz na objekt, se kterým databáze v daném kroku pracuje. Počet řádek načtených v rámci daného kroku. Počet bytů načtených v rámci operace. Cost CPU Relativní náročnost dané operace, velmi užitečné pokud chceme hledat slabá místa databáze. Očekávaná náročnost operace z pohledu procesoru. Operation Prováděná operace v rámci jednoho kroku exekučního plánu. Name Rows Bytes Odkaz na objekt, se kterým databáze v daném kroku pracuje. Počet řádek načtených v rámci daného kroku. Počet bytů načtených v rámci operace. 19
Exekuční plán je velmi komplexní popis operací prováděných databází. V příkladu a popisu uvedeného exekučního plánu jsem uvedl nejzákladnější informace, abych alespoň stručně nastínil jeho možnosti. Díky exekučnímu plánu dokážeme analyzovat všechny provedené operace k danému příkazu a dokážeme zjistit, která z operací je nejnáročnější, na tu se zaměřit a vyřešit dané slabé místo databáze. Proto pokud děláme výkonnostní testy, jedním ze základních nástrojů pro zjišťování průběhu je právě exekuční plán. 20
2.4. Indexy Indexy jsou pomocné databázové objekty, které zrychlují přístup k tabulkám. Nejjednodušší vysvětlení je na příkladu rejstříku knihy. Pokud čtenář hledá v knize pojem, podívá se do rejstříku, než aby pročítal řádek po řádku celou knihu a hledal daný pojem. Na úplně stejném principu fungují i indexy. Indexy jsou také seřazeny a neobsahují odkaz na stránku, ale odkaz na rowid. Indexy nám umožňují zrychlovat vyhledávání v tabulkách, ale bohužel na úkor vkládání, úprav a mazání dat, protože nemanipulujeme pouze s daty, ale zároveň i s indexy. Indexy jsou přiřazeny vždy k jednomu nebo více sloupcům a je důležité se zamýšlet, nad kterými sloupci vytvoříme index. Pokud bychom vytvářeli indexy automaticky nad každým sloupcem, vedlo by to ke zpomalení databáze, ne k jejímu zrychlení. Indexy jsou logicky i fyzicky odděleny od dat tabulky, proto je možné je vytvářet nebo mazat aniž bychom změnili data databáze. Pokud ale smažeme index, který databáze používala k přístupu k datům, pak se prodlouží přístupová doba k těmto datům. Společnost Oracle doporučuje vytvářet indexy nad primárními klíči tabulky a nad klíči, které odkazují na jiné tabulky. Obecné doporučení zní, aby indexy byly vytvořeny nad těmi sloupci, které často používáme pro prohledávání dat v tabulce. (4) Je důležité si uvědomit, že i v případě, kdy máme index nad tabulkou vytvořený, nemusí se využít pro přístup k datům. Proto bezhlavé přidávání indexů do tabulky není žádoucí. 2.4.1. B-Tree index Tento typ indexu je nejčastějším typem a je doporučováno ho používat pro sloupce v tabulce, které obsahují velký počet řádků a mají velkou kardinalitu dat (pojem kardinality bude popsán níže). Ideální případ použití je u tabulky, která obsahuje velký počet řádků, ale na základě dotazu do této tabulky je vybrán relativně malý počet dat. V takovém případě je index efektivně využit. Nad tímto typem sloupců se doporučuje indexy vytvářet. Pro který sloupec tabulky index použít? Jednak pro sloupec, který je často používán pro prohledávání dat a jednak na základě určení selektivnosti. Pojem selektivnosti velmi úzce souvisí s pojmem kardinality. 21
Nejprve bude vysvětlen pojem kardinality. Ta určuje, jak různorodá data databáze obsahuje. Typickým příkladem dat s nízkou kardinalitou je určení pohlaví, kde jsou pouhé dva typy hodnot - muž a žena. Na druhou stranu typickým příkladem dat s vysokou kardinalitou je unikátní primární klíč tabulky, který jednoznačně určuje jednotlivé řádky. V takovém případě je kardinalita vysoká a počet řádek udává počet hodnot typů dat. Proto se indexy nad primárními klíči vytváří v databázovém systému Oracle automaticky. Pojem selektivita udává procentuálně, kolik je vráceno dat pro jednu unikátní hodnotu vzhledem k celkovému počtu dat v tabulce. Tedy čím je vyšší kardinalita větší rozrůznění dat, tím je menší selektivita menší počet dat, které lze dostat pro jednu hodnotu. (5) Nejlépe pojem selektivity popíše definice: počet unikátní hodnoty selektivita= celkový počet záznamů Pokud toto pravidlo je použito na výše zmíněný příklad pohlaví a pokud je předpoklad rozdělení mužů a žen 50 na 50, pak je pro muže i ženy selektivita 50 %. Pro takto vysokou selektivitu rozhodně nelze doporučit vytvoření indexu. Druhým příkladem je unikátní primární klíč, zde za předpokladu že tabulka obsahuje 100 řádků, pak selektivita vychází na 1 %. Obecně pokud má být vytvořen b-tree index, pak selektivita daného sloupce by měla být maximálně 10 %. Pro zjištění selektivity daného sloupce tabulky může být použit tento příkaz: SELECT COUNT (DISTINCT first_name) / COUNT (first_name) * 100 selectivita FROM emp; V uvedeném příkladu je selektivita počítána na sloupci first_name tabulky emp. Je patrné, že při generování dat je důležité zachovat pravděpodobnost výskytů jednotlivých hodnot. Proto pokud jsou vygenerována data, kde jsou pouze 3 typy hodnot a navíc každá hodnota má pravděpodobnost výskytu 1/3, žádný hodnověrný výkonnostní test s takovými daty nelze provést. 22
Bylo vysvětleno, kdy je či není vhodné b-tree index použít. V následující kapitole bude popsán způsob uložení b-tree indexu v databázi, který je důležitý pro pochopení nutnosti přepočítávání indexů. Jak název napovídá, tento index je založen na principu b-stromu. Tento typ datové struktury se skládá z kořene (root), uzlů (node) a listů (leaf). Viz obrázek 3 níže. Obrázek 3: B - tree index kořen 122 uzly 65 136 151 listy 16 Rowid 35 Rowid 65 Rowid 66 Rowid 97 Rowid 122 Rowid 123 Rowid 135 Rowid 136 Rowid 139 Rowid 150 Rowid 151 Rowid 160 Rowid 190 Rowid 650 Rowid Zdroj: Vlastní zpracování Visio 2007 podle zdroje: http://www.dba-oracle.com/art_9i_indexing.htm Jak kořen, tak uzly mohou obsahovat větší počet hodnot. Tyto hodnoty jsou seřazeny, a pokud má kořen (uzel) n hodnot, pak má vazby na n+1 uzlů (listů). (Obrázek b-tree indexu je trochu zjednodušený, b-tree index může obsahovat větší počet vrstev uzlů.) Hodnoty v dětech (podřízených uzlech/listech) mají stejnou nebo nižší hodnotu, pokud jsou vlevo a vyšší hodnotu pokud jsou vpravo oproti svému předchůdci (uzlu/kořenu, ze kterého vazby vycházejí). Na základě těchto pravidel se vytváří datová struktura b-strom. Výpočet hloubky jednotlivých větví (počet zanoření od kořene k listu) je trochu složitější a není předmětem této bakalářské práce. Jen pro úplnost doplním, že pro efektivní vyhledávání v b-stromu je potřeba, aby hloubka stromu jednotlivých větví se vzájemně lišila maximálně o 1. Pokud tento rozdíl hloubky v jednotlivých větvích b-stromu je výrazně větší, pak je potřeba indexy přepočítat. Jednotlivé hodnoty se v b-stromu vyhledávají tak, že se strom postupně prochází od kořene a porovnávají se jednotlivé hodnoty s hodnotou hledanou. V momentě kdy se dojde do 23
nejnižší části b-stromu do listů, tak se zde hledaná hodnota buďto nenachází nebo nachází a v listě je odkaz na rowid řádku, kde je hodnota uložena. Teď je možné si uvědomit, proč je neefektivní vytvářet b-tree index nad sloupci s nízkou kardinalitou. Pokud by se nad sloupcem pohlaví vytvořil b-tree index, pak by v tomto b- stromu byl pouze kořen a dva listy, viz obrázek 4. Každý z těchto listů by obsahoval polovinu řádků tabulky, které by vraceli při zvolení dané hodnoty. Ve výsledku by tedy byl zvolen nejprve přístup indexový a následně by byla data postupně procházena. V takovém případě by bylo buď vhodnější nepoužít index žádný, pak by databáze k datům přistupovala přes full table scan nebo by byl použít bitmap index (popsán v následující kapitole). Tento příklad je pouze hypotetický, protože pokud by byl vytvořen index nad těmito daty, optimizer by spočítal neefektivnost indexového přístupu a použil by rovnou full table scan. Obrázek 4: B - strom s nízkou kardinalitou Pohlaví Muž Žena Zdroj: Vlastní zpracování Visio 2007 B-tree indexy můžeme vytvářet různými způsoby. Jedním ze způsobů je kompozitní index. Kompozitní index Tento typ indexu se vztahuje ke dvěma sloupcům. Je vhodný pro případy, kdy často používáme kombinaci dvou sloupců současně pro vyhledávání. Ale pozor, na druhou stranu je mylné se domnívat, že pokud je zvolen kompozitní index, tak že může být použit stejně, jako dva nezávislé indexy nad dvěma sloupci. Navíc při volbě sloupců záleží na tom, v jakém pořadí je zvolíme. (6) Uvedu a popíšu příklad kompozitního indexu: CREATE INDEX composite_index ON emp (surname, first_name); 24
V tomto příkladě je vytvořen index, který je nad sloupci surname a first_name a pokud je použit příkaz: SELECT * FROM emp WHERE surname='novak' AND first_name='jan'; je použit index tak, jak předpokládáno. V případě, že by bylo potřeba vyhledávat pouze podle jména, například takto: SELECT * FROM emp WHERE first_name='jaroslav'; index nebude použit! Index nebo spíše jeho část se použije pouze v případě, kdy je v tabulce vyhledáváno pouze podle příjmení. Kompozitní indexy se mohou skládat i z více než dvou hodnot a to tak, aby tyto skupiny sloupců co nejvíce usnadňovaly vyhledávání. Proto se doporučuje používat kompozitní indexy tak, aby sloupce v nich obsažené byly řazeny sestupně podle důležitosti (četnosti použití). Indexovaná funkce Dalším způsobem jak vytvořit b-tree index je indexovaná funkce, která umožňuje vytvořit index nad hodnotou vrácenou z funkce. Příklad: CREATE INDEX function_index ON emp(lower(surname)); V tomto příkladě se vytváří index nad funkcí lower, díky které se zjednoduší vyhledávání příjmení. Lze vynechat kontrolu, zda nebyla příjmení zadána s velkým písmenem. Ukázka dotazu nad tabulkou: SELECT first_name, surname FROM emp WHERE lower(surname) = 'muller'; Obecně indexové funkce zjednodušují používání funkcí v rámci dotazů nad tabulkami. Mohou se tak jednoduše třídit data, dále se nemusí řešit velká a malá písmena v zadaných datech nebo se mohou přidat vlastní funkce, které zjednoduší zpracování dat. Používání indexů má jeden háček, a tím je hledání řádků, které obsahují hodnotu null. Pokud se zadá příkaz, který má vrátit všechny řádky, kde v některém ze sloupců je 25
podmínka null, vždy databáze k tabulce přistoupí přes full table scan. Tento háček lze jednoduše řešit pomocí indexových funkcí. Tato funkce nahradí null hodnotu řetězcem 'null', následně jsou pak hledány v tabulce řetězce null. Předpokladem je, že se jedná o proměnnou typu řetězec. U sloupců s jinými datovými typy by bylo řešení analogické. Toto nahrazení můžeme provést pomocí funkce nvl, která vrací předdefinovanou hodnotu, pokud jí nebyla předána žádná jiná hodnota. Ukázka použití pomocí index funkce: CREATE INDEX indx_find ON emp(nvl(degree, 'null')); Díky takto použité indexové funkci je možné za pomocí indexů najít uživatele, kteří nedosáhli žádného titulu (degree). Příklad: SELECT first_name, surname FROM emp WHERE nvl(degree, 'null') = 'null'; I když je vytvořen index nad sloupcem správným způsobem, nemusí být použit pro vrácení dat. Databáze přístupy k datům vyhodnocuje a vrátí vždy data předpokládaným nejefektivnější způsobem. Výběr nejefektivnějšího přístupu k datům obstarává optimizer (popsán v samostatné kapitole). Proč databáze nevolí přístup přes b-tree indexy, i když se to může zdát výhodné? Důvody: 1. Malé množství řádků je rychlejší přistupovat k malému množství dat přes full table scan než přes index scan 2. Velké množství řádků, ale načítání velkého množství dat i v tomto případě je výhodnější použít full table scan, protože FTS načítá data po blocích na rozdíl od IRS, který načítá data sekvenčně Ze zmíněných důvodů je zřejmé, že je důležité nejen volit indexy pro správné sloupce, popřípadě kombinace sloupců, ale i správně psát dotazovací příkazy pro databázi. Dále je velmi důležité nepodceňovat správu a údržbu databáze, díky které ji pak můžeme snadněji optimalizovat nebo odstraňovat vzniklé problémy. 2.4.2. Bitmap index Dalším typem indexu je bitmap index. Tento typ indexu je stejně jako b-tree index uložen v b-stromu. Tento index je vhodný pro tyto typy dat: 26
Data s nízkou kardinalitou. Data, kde převládají read-only operace nebo úpravy dat jsou jen výjimečné. Data, kde často vyhledáváme pomocí OR podmínek. Jak bylo uvedeno, bitmapové indexy jsou uloženy také v b-stromu. To zaručuje rychlé vyhledávání jednotlivých hodnot. V čem je ale tento index od b-tree indexu odlišný, je způsob uložení hodnot v listech b-stromu. V listu je uložena bitmapa hodnot místo seznamu rowid. Tato bitmapa se vztahuje k zadanému rozsahu řádků, které jsou uloženy v paměti. V bitmapě má nastavený bit na 1 ten řádek, který zadanou hodnotu obsahuje a 0 ten řádek, který zadanou hodnotu neobsahuje. Princip bitmap indexu viz Obrázek 5. Obrázek 5: Bitmap index Zdroj: http://www.siue.edu/~dbock/cmis565/module12-indexes.htm Vysvětlení principu tohoto indexu bude nejjednodušší na příkladu. Snažíme se najít všechny muže v naší firmě. Průchod b-stromem bude rychlý, jsou v něm pouze dva listy. V listu s hodnotou muže bude bitmapa mapující hodnoty v paměti a bude obsahovat jedničky tam, kde se hodnota muž nachází a nuly, kde se nenachází. Bitmapa nám vrátí sadu řádek, které obsahují hledanou hodnotu. Jak tento index vypadá, je možno se podívat 27
na obrázku níže, kde je podrobně rozkreslen. Díky tomuto nízko úrovňovému přístupu k datům (přístup přes bitmapu) je načítání dat rychlejší. Způsob uložení a pracování s bitmapou je speciálně navržen společností Oracle a patentově chráněn. (4) Jediným háčkem bitmapových indexů je jejich případná úprava. Sice není tak velký problém data měnit z jedné hodnoty na jinou. To znamená pouhou změnu bitů z nuly na jedničku. Ale pokud bude potřeba přidat novou hodnotu do sloupce, pak se musí přepočítat úplně celá indexová struktura. To není jednoduchá operace. Z tohoto vyplývá nemožnost použití těchto indexů v OLTP databázích, které jsou zaměřeny na snadnou modifikaci dat. 2.4.3. Indexově organizované tabulky (Index organized tables) Indexově organizovaná tabulka se vzdáleně týká indexů a jedná se o speciální strukturu dat, kam se ukládá tabulka. Ale na základě principu indexů byla vyvinuta. Tento typ tabulky vznikl na základě těchto myšlenek: Je neefektivní vytvářet index nad každým sloupcem tabulky Zároveň byl využit koncept FFIS (Fast full index scan), kdy přístup přes indexy k datům je velmi rychlý. Tak vznikl koncept index organized table, kdy řádky tabulky nejsou uspořádány podle rowid, ale jsou vloženy do b-stromu, kde jsou uspořádány podle primárních klíčů. Zároveň v každém listě jsou uloženy i neklíčové hodnoty řádku, proto přes primární klíč přistupujeme rovnou ke všem hodnotám. To zaručuje velmi rychlý a efektivní přístup. Bohužel tento koncept má i nějaké nedostatky. Například tento typ tabulek nelze ukládat do clusterů. (4) Pokud by se měřila rychlost přístupů, mohlo by být na první pohled zarážející, že tabulky s indexy oproti index organized table nejsou zas o tolik pomalejší. Pak by jejich jedinou praktickou výhodou byla úspora místa. Protože indexově organizovaným tabulkám stačí ukládat jedinou strukturu dat oproti ukládání samotných dat a navíc jejich indexů. Ovšem index organized table lze dále optimalizovat, a to následujícími způsoby: 28
Dodefinováním overflow area (oblast přetečení), díky které je v listě uloženo jen omezené množství dat a data, která se do listu nevejdou, jsou uložena na jiném místě. Přidáním sekundárních indexů, které nám dále zrychlují přístup k datům. Využití tohoto typu tabulek je doporučováno pouze pro kratší řádky, kde v jednotlivých řádcích není uloženo větší množství dat. Částečně lze toto optimalizovat pomocí overflow area, ale i přesto je lepší používat tyto tabulky spíše pro ukládání krátkých řádků. Dalším omezením je nemožnost ukládat datové proměnné typu long, místo něj musíte použít datový typ lob. (7) 29
2.5. Optimizer Aby byla jasná úloha optimizeru v rámci zpracování sql příkazu, bude nejprve popsán obecný popis komponent, které s optimizerem souvisí. Nejprve uživatel zadá sql dotaz, ten je zkontrolován parserem z hlediska korektnosti syntaxe, zda dotaz obsahuje všechny povinné položky a zda mají korektní formu. Pak přichází na řadu optimizer, který vybere způsob optimalizace vykonání dotazu na základě toho, zda existují statistiky nebo ne. Pokud statistiky vygenerovány v databázi jsou, použije se výpočet optimální cesty pomocí CBO (Cost-Based Optimizer) přes statistiky. Pokud ne, CBO použije dynamic sampling pro dopočítání chybějících nebo neúplných statistik. V případě požadavku na zpětnou kompatibilitu může být zvolen i druhý typ optimizeru RBO (Rule Based Optimizer). (8) Obrázek 6: Diagram zpracování sql dotazu Zdroj: http://docs.oracle.com/cd/b10501_01/server.920/a96533/optimops.htm 30
Optimizer vrací návrh nejefektivnějšího způsobu jak načíst data z databáze. Následující komponenta Row Source Generator na základě tohoto vygenerovaného postupu vrací exekuční plán. Exekuční plán popisuje podrobně načítání a zpracování dat. Posledním logickým krokem je vykonání příkazu podle exekučního plánu pomocí SQL Execution Engine ( stroje pro vykonání sql ). Postupné kroky tohoto procesu je možné si prohlédnout na obrázku 6 diagramu zpracování sql dotazu. Databáze může volit různé přístupy k tabulkám. Může být zvolen přístup full table scan nebo některý z indexových přístupů. Optimizer volí na základě znalosti tabulek z těchto přístupů ten nejefektivnější. V jeho konfiguraci můžeme zvolit jeden ze dvou modů cílů optimalizace. A to buď nejlepší propustnost (the best throughput) nebo nejlepší dobu odezvy (the best response time). Na příkladech bude vysvětlen rozdíl těchto dvou modů. (9) Pokud zvolíme nejlepší propustnost, optimizer volí takové přístupy, aby všechna data dostal uživatel co nejdříve. Tento model je volen tehdy, kdy uživatel požaduje ihned načíst všechna data z databáze v nejkratším možném intervalu. Typickým a často voleným přístupem v tomto modu je full table scan, který dokáže data načítat po blocích, a tím načíst velmi rychle velké množství dat. Druhý mode (nejlepší doba odezvy) je volen tehdy, kdy uživatel systému chce mít k dispozici co nejdříve první řádek nebo prvních pár řádků. Typickým přístupem, který je volen v tomto modu, je index scan. Tento přístup je schopen velmi rychle vrátit první požadovaný řádek. Z řádků napsaných výše by mohlo vyplývat, že přístup full table scan je výhradně volen v modu optimizeru nejlepší propustnost a index scan v modu nejlepší doba odezvy. Takto přístupy voleny nejsou. Optimizer má nastaveny určité preference, díky kterým volí jednotlivé přístupy, ale rozhodně nejsou v jednotlivých modech voleny pouze některé přístupy, to by databázový přístup k datům nebyl dostatečně flexibilní a optimalizovatelný. 2.5.1. RBO (Rule based optimizer) a CBO (Cost based optimizer) Jak bylo zmíněno výše, Rule based optimizer je volen z důvodu požadavku zpětné kompatibility. Naproti tomu Cost based optimizer je novější a technologicky vyspělejší. I společnost Oracle doporučuje používat CBO. RBO by měl být použit pouze ve 31
výjimečných případech a můžeme očekávat, že v budoucích verzích bude z databáze odstraněn. Navíc Cost based optimizer podporuje rozdělené tabulky, materializované pohledy a další. Krátce k Rule based optimizeru: Tento typ optimizeru je založen na pravidlech. Jednotlivé přístupy jsou vybírány podle pořadí, a zda je možné je nad tabulkou vykonat. Tedy pokud je možné zvolit více přístupů, je zvolen ten, který je v pořadí dříve. Část seřazeného seznamu přístupů, tak jak jej využívá RBO: Tabulka 2: Pořadí přístupů, které jsou voleny v RBO Pořadí Přístup 1. Jednotlivé řádky přes Rowid... 4. Jednotlivé řádky přes unikátní hodnotu nebo primární klíč... 8. Přes kompozitní indexy 9. Indexový přístup... 15. Přes full table scan Z tabulky je tedy patrné, že pokud bude možné zvolit přístup přes index, bude vždy zvolen, a to i v případě, kdy by byl přístup přes full table scan efektivnější. Proto je doporučováno tento typ optimalizátoru nepoužívat. (10) 32
Cost based optimizer je založen na ohodnocování jednotlivých přístupů do databáze na základě statistik tabulek, sloupců, indexů a dalších (statistiky popsány v samostatné kapitole). Na základě hodnot těchto statistik CBO navrhne exekuční plány a zároveň je ohodnotí. Databáze pak z plánů vybere ten nejvýhodnější, který je proveden pro zpracování zadaného příkazu. Součástí hodnocení statistik není jen složitost datové struktury databáze, ale i možnosti jednotlivých zdrojů hardwaru serveru, například propustnost I/O nebo výkonnost CPU. (8) Samotný CBO se skládá z několika částí, které si vzájemně dělí jednotlivé činnosti zpracování hodnocení přístupů. Popis těchto částí je však nad rozsah této práce. 33
2.6. Statistiky Statistiky jsou pomocná data, která podrobně popisují strukturu databáze. Popisují tabulky, sloupce, indexy atd. Na základě těchto dat, je pak optimizer (konkrétně CBO) schopen najít nejefektivnější exekuční plán. Statistiky popisují různé objekty v databázi a podle toho se dělí na: statistiky tabulek (table statistics), statistiky sloupců (column statistics), statistiky indexů (index statistics), statistiky výpočetních možností serveru (System statistics). Ke spravování statistik se používá balíček DBMS_STATS. Tento balíček neslouží jen ke sběru statistik, ale také k importu, exportu popřípadě i jejich smazání nebo zamknutí, pro nemožnost jejich úpravy. Jednoduchý export nebo import statistik je umožněn díky tomu, že statistiky jsou uloženy v databázových slovnících (11). Je zřejmé, že data se v databázi často mění a pokud by jednou vygenerované statistiky měly být používány delší dobu, ztratila by se jejich schopnost optimalizovatelnosti. Proto je důležitá jejich pravidelná správa. O tu nejzákladnější se stará automatic optimizer statistics collection. Tato funkcionalita se aktivuje následujícím příkazem: EXECUTE DBMS_AUTO_TASK_ADMIN.ENABLE( client_name => 'auto optimizer stats collection', operation => NULL, window_name => NULL); Tato komponenta monitoruje počet operací INSERT, UPDATE, DELETE a pokud je vyhodnoceno, že statistiky již neodpovídají datům, jsou přepočítány. Většinou se statistiky přepočítávají, pokud se změní víc jak 10 % dat v tabulce. Tato automatická detekce je postačující, pokud není databáze z velké části neustále modifikována. Pokud ano, je pak důležité statistiky aktualizovat navíc manuálně. Manuální sběr statistik je nutný v případě, kdy třeba změníme podstatnou část tabulky pomocí bulk operací. (Pojem bulk operace je vysvětlen v kapitole 3.1. Úvod do PL/SQL.) Proto je nutné 34
při těchto operacích nezapomínat na statistiky. Vynucení přepočítání statistik způsobí následující příkaz: EXECUTE DBMS_STATS.GATHER_TABLE_STATS(ownname=>tableowner, tabname=>'tablename', method_opt=>'for ALL INDEXED COLUMNS'); Na základě tohoto příkazu jsou přepočítány statistiky nad tabulkou tablename vlastněnou uživatelem tableowner. Statistická data jsou posbírána ze všech jejích sloupců. Tento příkaz patří do skupiny statistiky tabulek. V příkladu je volána metoda GATHER_TABLE_STATS. Pokud by byla použita jiná procedura balíčku DBMS_STATS, například procedura GATHER_SYSTEM_STATS, pak by došlo k vygenerování systémových statistik. U této procedury by samozřejmě byly nastaveny jiné parametry (11). V ukázkových příkladech byl vždy použit balíček dbms_stats, který ale není jediný, který můžeme pro práci se statistikami volat. Ale jeho používání je doporučováno společností Oracle. Kromě tohoto balíčku lze například použít příkaz ANALYZE, který je v databázovém systému udržován již jen z důvodů zpětné kompatibility. Na konci této kapitoly jsou zmíněny funkcionality, které se statistikami souvisí už jen okrajově, ale jsou důležité pro pochopení souvislostí. Jedná se o dynamic sampling a histogramy. Dynamic sampling Tato funkcionalita byla zavedena od verze 10g a vyřešila velký problém optimalizace pro tabulky, kde nejsou vygenerované statistiky. Do této verze v takovém případě byl použit RBO, který nemusel vrátit optimální exekuční plán. Dynamic sampling umožňuje dopočítat chybějící data statistik a poskytnout tak v každém případě co možná nejvíce optimální exekuční plán. CBO se během každé kompilace dotazu rozhoduje, do jakého stupně použije dynamic sampling. Toto rozhodování je založeno na mnoho faktorech. Ale hlavním důvodem proč optimizer volí tuto metodu, jsou zastaralé nebo chybějící statistiky. (8) 35
Histogramy Histogramy jsou speciálním druhem statistik a jsou zde uvedeny pro úplné doplnění výčtu. Histogramy jsou důležité pro případy, kdy potřebujeme zjistit přesnější rozmístění dat v databázi. Tento typ statistik nám dává přehled, jak jsou data asymetricky rozložena a na základě těchto dat se optimizer rozhoduje, který přístup zvolí. 36
2.7. Hinty Hinty nám umožňují ovlivňovat běh exekučního plánu. Je možné tak databázi nadefinovat, který přístup má zvolit. Vychází z předpokladu, že uživatel má více informací o databázi než optimizer, proto může zvolit efektivnější přístup. Například máte podrobnější informace o selektivnosti indexů pro určitý SQL příkaz. Hinty mohou být vyvolány jen v případě, kdy používáte CBO a pokud je zadáte v příkazu ve špatném formátu, pak budou ignorovány a neprovedou se. Pozor, optimizer neupozorňuje na to, že nebyly použity. Hinty se zadávají do SQL příkazů za příkazy UPDATE, SELECT nebo DELETE. Přes hinty lze ovlivnit: Přístup k tabulkám. Jakým způsobem se mají tabulky spojit. Určit pořadí spojení tabulek. Nastavení optimalizačních parametrů. Příklad použití hintu: SELECT /*+ FULL(users) */ firstname, surname FROM users WHERE firstname = 'Hana'; O tabulce users víme, že obsahuje velký počet uživatelů s křestním jménem Hana, proto pomocí hintu optimizer nastavíme tak, aby k této tabulce přistoupil přes FTS, který v tomto konkrétním případě bude nejrychlejší. (12) 37
3. PRAKTICKÁ ČÁST - GENEROVÁNÍ DAT Praktická část bakalářské práce se zaměřuje na samotný popis Nástroje na tvorbu testovacích dat pro zátěžové testování DB. V této části bude popsán jazyk PL/SQL, ve kterém je nástroj napsán. Dále zde je zmíněno, odkud byla získána zdrojová data a jak byla upravena. A v neposlední řadě bude rozebrán princip, jakým nástroj generuje náhodná data pro zátěžové testy. Vysvětlení principů je doplněno o ukázky zdrojového kódu nástroje. 3.1. Úvod do PL/SQL V kapitole Relační databáze byl zmíněn jazyk SQL a skupiny příkazů, které obsahuje. Díky tomuto jazyku je možné navrhnout i spravovat databáze. Pokročilejší programování nebo práce s větším množstvím dat ale není možná. Pro tyto případy společnost Oracle vyvinula procedurální nadstavbu jazyka SQL, PL/SQL. Jiné databázové technologie mají také své procedurální jazyky, například Microsoft SQL server má Transact SQL neboli T-SQL. (13) Tento krátký úvod se zaměřuje na základy programovacího jazyka PL/SQL, které jsou nutné pro následné pochopení principu generátoru dat pro zátěžové testy. 3.1.1. Balíčky, procedury a funkce Program v PL/SQL je složen z bloků, které mohou být vnořeny jeden do druhého. Každý blok spouští jednu logickou akci v programu. Blok se skládá z následujících částí: a) DECLARE v této části bloku jsou definovány jednotlivé proměnné a jejich datové typy b) BEGIN výkonná sekce v této části se vykonávají funkce, procedury a SQL příkazy (tato jediná část je v bloku povinná) c) EXCEPTION v této části se zpracovávají výjimky, ke kterým došlo ve výkonné části Ve výkonné části je povolena skupina příkazů DML, to znamená SELECT, INSERT, 38
UPDATE, DELETE a další příkazy pro správu transakcí. Příkazy pro definování struktury databáze (příkazy DDL) například CREATE, ALTER nejsou ve výkonném bloku povoleny. (14) Tímto způsobem jsou definovány tak zvané anonymní bloky, které jsou zpravidla vykonány pouze jedenkrát. Dalším typem bloků jsou pojmenované bloky. Pojmenované bloky je možné opakovaně používat a tyto bloky jsou procedurami a funkcemi, které jsou známy ze strukturovaného programování. Jen pro zpřesnění: rozdíl mezi funkcí a procedurou je ten, že funkce na rozdíl od procedury vrací výsledek. V dalším textu jsou zmiňovány jen funkce, ale jsou tím míněny funkce i procedury. (15) Příklad jedné funkce z praktické části práce: CREATE OR REPLACE FUNCTION my_divide (n1 IN number, n2 IN number) RETURN NUMBER IS BEGIN n3 NUMBER; n4 NUMBER; n4 := MOD(n1,n2); n3 := (n1-n4)/n2; IF n4 > 0 THEN n3 := n3+1; END IF; RETURN n3; EXCEPTION END; WHEN ZERO_DIVIDE THEN RETURN n1; V praktické části práce byla potřeba funkce, která by dělila dvě čísla tak, aby vrácený výsledek byl vždy zaokrouhlen nahoru. Standardní funkce pro dělení výsledek automaticky zaokrouhluje nahoru nebo dolů, podle hodnoty za desetinnou čárkou. Funkce my_divide převezme dva parametry n1 a n2. Nejprve zjistí celočíselný zbytek po dělení n1, n2. Tento zbytek je poté odečten od n1 a upravená hodnota n1 je dělena hodnotou n2, pokud byl celočíselný zbytek větší než jedna, je k výsledku přičtená jednička. 39
Tento příklad obsahuje všechny možné části, které byly pro blok definovány, a je na něm vidět způsob použití jednotlivých bloků. Další výhodou definovaných funkcí je možnost jejich použití přímo v SQL příkazech. Například: SELECT CONCAT (first_name, surname) whole name FROM employees; Tento příkaz vrátí místo dvou sloupců jména a příjmení sloupec jeden, který obsahuje celé jméno. Jiným způsobem je použití funkcí v rámci indexů. Příklady, jak použít funkce v indexech, byly popsány v kapitole Indexovaná funkce. Bylo popsáno, jak vypadají anonymní a pojmenované bloky používané v programovacím jazyce PL/SQL. Pokud by byly používány buď jen funkce, nebo dokonce pouze anonymní bloky, bylo by náročné se v kódu vyznat. Proto jsou v tomto jazyce zavedeny balíčky, které obsahují logicky související funkce a procedury. Balíčky se skládají z hlavičky a těla. V hlavičce se deklarují veřejné funkce a v těle jsou pak definovány funkce soukromé. Soukromé funkce jsou deklarované i definované v jeho těle a nejsou přístupné z vnějšku. Veřejné funkce pak mohou být volány uživatelem nebo funkcemi i z jiného balíčku. Příklad jednoduché definice hlavičky a těla: CREATE PACKAGE my_package IS FUNCTION sum (number1 NUMBER, number2 NUMBER, number3 NUMBER); END my_package; CREATE PACKAGE BODY my_package IS FUNCTION sum (number1 NUMBER, number2 NUMBER, number3 NUMBER) IS BEGIN RETURN number1 + number2 + number3; END; END my_package; 40
Uvedený balíček obsahuje jedinou veřejnou funkci, která slouží pro sečtení tří čísel. Tento balíček neobsahuje žádnou soukromou funkci. Tento příklad je pouze názorný, a pokud by měla být v balíčku pouze jedna veřejná funkce, nemělo by význam balíček zakládat. Generátor náhodných dat, který je předmětem této bakalářské práce, je napsán pomocí funkcí, které jsou vloženy do balíčků. Jejich přehled: 1. fetch_source_data_package balíček sloužící k načtení zdrojových dat, na základě kterých se generují náhodná data, 2. log_package balíček sloužící k logování událostí vzniklých během procesu generování dat, 3. random_data_package balíček ke generování specifických dat, která mají specifické omezující parametry (například generování rodného čísla), 4. random_table_package balíček, který slouží k vytvoření tabulky a následnému jejímu naplnění generovanými daty, tento balíček je stěžejním a jednotlivé funkce a procedury z ostatních balíčků jsou volány odsud. Pro zrychlení operací s velkým množstvím dat nabízí PL/SQL bulk operace. Tyto operace snižují počet přepínání mezi enginem pro SQL a enginem pro PL/SQL. Jednotlivé enginy slouží k vykonávání buď SQL nebo PL/SQL kódu. Princip přepínání enginů viz obrázek 7. Obrázek 7: PL/SQL a SQL engine Zdroj: http://docs.oracle.com/cd/b19306_01/appdev.102/b14261/overview.htm 41