RDBMS uitgelegd

Eén steen tegelijk

Een paar jaar geleden, op een warme vochtige dag in augustus, besloot ik een bakstenen muurtje te gaan bouwen in mijn achtertuin. Stenen, cement en tijd waren geen probleem, maar de metselaar wel. Ach, hoe moeilijk kon metselen eigenlijk zijn? Ik kocht het Grote Metselboek, las de inleiding en begon.

Nadat ik de boel had uitgezet maakte ik eerst een zandbed. Vervolgens berekende ik hoeveel bakstenen ik nodig had en bestelde ze. Ik gaf daarbij aan dat ze donderdag thuis afgeleverd moesten worden en dat ze zover mogelijk achterin op de oprijlaan richting tuin gestort moesten worden. Slim bedacht vond ik.

Ik kwam die donderdag vroeg thuis na het werk om de stenen, je raadt het al, vooraan op de oprijlaan op een enorme stapel aan te treffen. Ik dacht: niet jammeren. We gaan de komende 3 dagen lekker metselen, ik zorg voor voldoende frisdrank en laat mijn dochtertje Karin een handje helpen.

De volgende ochtend pakte ik een aantal flessen fris, trok de werkhandschoenen aan en ging naar de tuin om te beginnen. Ik riep Karin en vroeg haar wat stenen te halen. Karin is 11. Ze zat op haar kamer en had er geen zin in maar deed het toch en kwam terug met een steen. Eén steen. Ze legde de steen neer en ging terug naar haar kamer.

Ik pakte de steen en plaatste die op het zandbed, keek om en zag geen stenen meer. En ook geen Karin. Ik riep haar weer en zij ging weer een steen halen. Ik wachtte en nam wat frisdrank. Toen zij terugkwam met de steen kon ik weer verder met mijn metselroutine. Met één steen.

Karin werkte in synchrone I/O. Ze haalde één steen tegelijk. En zolang ze die steen aan het halen was werkte ik synchroon met haar, één steen tegelijk. In RDBMS-land kost het synchroon wachten op I/O tijd gemiddeld 0,02 seconden, wat behoorlijk snel is. Tenzij je veel stenen nodig hebt. Hoewel zelfs de metselkampioen maar één steen tegelijk kan leggen gaat hij niet elke keer naar die stapel stenen toe om er maar eentje te pakken. Hij zorgt ervoor dat als hij een steen nodig heeft die op pakafstand van hem vandaan ligt.

Het was augustus en het was warm. Er moest een betere manier zijn. Mijn broer Hans logeerde dat weekend bij me en zat een beetje voor de tv te hangen. Ik riep hem en vroeg of hij een paar stenen voor me wilde halen. Hij kwam naar me toe, keek wat om zich heen en liep naar de schuur. Daar vond hij een kruiwagen en liep daarmee naar de stapel stenen aan het begin van de oprijlaan. Hij stapelde de kruiwagen helemaal vol. Hij kwam tot 32 stenen. Hij liep naar mij terug en kieperde de stenen achter me opde grond neer. Hij was langzaam. Het kostte hem 0,064 seconden, meer dan 3 keer zo lang als Karin. Maar hij kwam wel terug met 32 stenen, dus gemiddeld was dat 0,002 seconden. En daarna ging hij weer terug om nog eens 32 stenen op te halen. En het maakte mij niets uit hoe lang hij daarover deed, omdat ik die eerste 32 stenen eerst moest wegwerken.

Toen Hans voor de tweede 32 stenen wegging deed hij asynchrone I/O, hij liep niet synchroon met mij. Een proces op de achtergrond. Zolang hij maar terug was voordat ik de 32 stenen opgebruikt had, maakte het mij niet uit hoe lang hij erover deed. Nadat hij de tweede 32 stenen uitgeladen had ging Hans even uitpuffen en een glaasje fris drinken. Zo kon hij de stand van de stapel stenen die hij inmiddels gemaakt had even bekijken. Hij was aan het multitasken. Hij stond eigenlijk te wachten totdat ik in die stapel van oorspronkelijk 64 stenen een bepaalde schijnbaar belangrijke steen had aangeraakt: de trigger voor hem dat er een nieuwe kruiwagen vol gehaald moest worden. Hij wilde niet te vroeg gaan, want misschien was ik wel van plan eerder te stoppen. Maar hij wilde ook niet te laat gaan wat tot gevolg kon hebben dat ik op hem zou moeten wachten.

De triggersteen bleek steen nummer 32 te zijn, halverwege de totale hoeveelheid. En toen ik die steen aanraakte liep hij gelijk weg, asynchroon, om er meer voor me te halen. Hans deed sequential prefetch. En met grote gezonde bufferpools kan elke RDBMS-taak die veel stenen nodig heeft profijt hebben van het gebruik van de kruiwagen, zolang de applicatie maar goed ontworpen is en gebruik maakt van sequentiële leesopdrachten.

In een RDBMS ( relationeel database management systeem ) is de eenheid van I/O de page in plaats van de steen, maar het concept is hetzelfde. Soms willen we slechts 1 page hebben (bijvoorbeeld: we hebben de unieke cliëntrij nodig om op een scherm te tonen). Maar andere keren hebben we meerdere rijen nodig (batchprocessen, online zoekopdrachten e.d.).

Als de database-optimizer op BIND-moment berekent hoeveel rijen nodig zijn, zoekt hij ook uit waar die rijen in de index en de tabel te vinden zijn en wie van de 2 (Karin of Hans) nodig is om die rijen op te halen.

Een rij ophalen op basis van unieke sleutel is gemakkelijk. Moet je één rij in één indexpage lezen om één rij in één datapage te vinden, dan moet je Karin dat laten doen. Maar queries die meerdere rijen in meerdere pages nodig  hebben zijn complexer. De optimizer moet uitzoeken waar die rijen staan, in welke page ze te vinden zijn, en of ze achter elkaar staan of willekeurig door elkaar.

Als het toegangspad een full indexspace scan of een full tablespace scan op een tabel van enige omvang behelst, is de oplossing eenvoudig. Als je alle rijen in de index of de tabel nodig hebt en je leest die rijen sequentieel, dan zal het RDBMS Hans kiezen om je te helpen. De keuze wordt ingewikkelder als je een index nodig hebt om de pointers te vinden  die wijzen naar de datarijen in de tabel. Als het dus het geval is dat je veel indexrijen moet lezen en dat je veel pointers of RID's af moet om al die datarijen op te halen, dan wil het RDBMS erg graag weten of die  RID's gaan wijzen naar opeenvolgende rijen die sequentieel gepositioneerd zijn in opeenvolgende pages. Of ze gaan wijzen naar een rij op page 500 en daarna naar een rij op page 2 en dan weer naar eentje op page 8000, enzovoorts?

Een RDBMS utility werkt de statistieken in de RDBMS-catalog bij. Deze worden gebruikt door de optimizer op BIND-moment om het beste toegangspad te bepalen. Een van de statistische gegevens, CLUSTERRATIO, vertelt de optimizer of de in het toegangspad potentieel te gebruiken index een Database-index is die min of meer gelijk loopt met de tabelrijen of dat het een Database-index is met een gedragsprobleem. Database-indexen met een lage clusterratio hebben gedragsproblemen als deze gebruikt worden om meerdere rijen op te halen. Een RDBMS vindt het niet erg om ongeclusterde indexen te gebruiken om een paar rijen op te halen. Immers, Karin haalt een page in ongeveer 20 milliseconden op, ongeacht waar die staat in de tabel. Zij leest page 5000 net zo snel als page 1. Maar als er meerdere rijen nodig zijn en elke rij in een andere page, ver weg van de voorgaande, staat, dan doen die synchrone wachttijden de I/O snel oplopen.

Processen die cursors openen om miljoenen rijen op te halen in indexgestuurde leesopdrachten, hebben Hans en zijn kruiwagen nodig om de pages te prefetchen in de bufferpool. Zulke processen moeten voordeel halen uit een geclusterde index, eentje die dicht op de volgorde van de datarijen in de tabel staat. Stel je voor dat de DBA een clusterindex op 2 kolommen heeft gemaakt: AFDELINGNR en  MEDEWERKERNR. Een regelmatig gereorganiseerde tabel zal praktisch op volgorde van de clusterindex liggen. Als de batch zo ingericht kan worden dat het sequentiële invoerbestand op volgorde van afdelingsnummer en medewerkersnummer gelezen wordt en dat er een cursor gedefinieerd wordt als ORDER BY AFDELINGNR, MEDEWERKERNR, dan kan Hans met behulp van zijn kruiwagen de datapages asynschroon prefetchen. Het programma zal meer draaien dan wachten en de job kan best eens eerder klaar zijn dan wordt verwacht.

Ik heb veel geleerd tijdens het muurtje bouwen. Ik heb geleerd dat die metselkampioenen die de stenen één voor één op elkaar leggen experts zijn op het gebied van het uitnutten van indexen met hoge clusterratio's en van sequential prefetch om op die manier I/O-wachttijden te verkleinen. En wat ik ook heb geleerd was dat zij hun muurtjes niet in augustus bouwen. Zij wachten tot het najaar als hun vrienden op bezoek komen. Zij stellen hulpjes aan voor Hans en voor de metselaar. Meerdere Hansen met meerdere kruiwagens die meerdere metselaars van stenen voorzien, CPU-parallellisatie, de ultieme muurtjes bouwmethode!