MaxDB führt zur Laufzeit allerlei Ausgleichsoperationen auf dem Speichermedium aus, um eine möglichst platzsparende Datenablage zu erreichen und gleichbleibende Geschwindigkeit zu erreichen. Den MaxDB-Anwendern ist die als Reorganisationsfreiheit bekannt. Doch wie im Blogeintrag “MaxDB’s Reorganisationsfreiheit” bereits erwähnt, kann man MaxDB anweisen, nicht alle Ausgleichsoperationen durchzuführen.
Bringt es etwas?
Ich habe mir heute ein kleines Programm geschrieben, daß einige, einfache Operationen durchführt um den Einfluß von DYNAMIC
zu bewerten. DYNAMIC
ist ein Attribut, welches bei der Tabellendefinition angegeben werden kann. Es bewirkt, daß MaxDB nur noch vereinzelt Ausgleichsoperationen durchführt. Dies beschleunigt Änderungsoperationen führt jedoch zu einem erhöhten Speicherbedarf.
Leider habe ich keine Angaben darüber gefunden, wie groß der Effekt von DYNAMIC
ausfällt. Deshalb habe ich selbst ein kleines Testprogramm geschrieben und nachgemessen. Ich habe mir keine besondere Mühe gemacht einen aussagekräftigen Benchmark zu entwickeln. Die Messergebnisse, die weiter unten aufgeführt werden, sollten nur als grobe Anhaltspunkte verstanden werden. Sie erheben keinen Anspruch in irgendeiner Weise sinnvolle Rückschlüsse auf reale Applikationen zu geben. Die Charakteristika einer Applikation stimmen in den seltensten Fällen mit denen einer Benchmark Suite überein. Eigene Test mit er eigenen Applikation sind deshalb unverzichtbar.
Tabellendefinition
Für den Test wurde eine einfache Tabelle mit einem Integer-Primärschlüssel und einigen Varchar-Spalten verwendet. Varchar wurde benutzt, um MaxDB zu zwingen bei Längenänderungen Ausgleichsoperationen auszuführen. Im Normalbetrieb führt eine Längenänderung zum Verschieben von Datensätzen auf einer Datenseite und zur Aktualisierung der Positionsliste, siehe “MaxDB’s Reorganisationsfreiheit”.
CREATE TABLE DYNAMIC_TEST ( pk INTEGER, a VARCHAR(100), b VARCHAR(100), c VARCHAR(100), d VARCHAR(100), e TIMESTAMP, PRIMARY KEY (pk) ) DYNAMIC CREATE TABLE STATIC_TEST ( pk INTEGER, a VARCHAR(100), b VARCHAR(100), c VARCHAR(100), d VARCHAR(100), e TIMESTAMP, PRIMARY KEY (pk) )
Alle Tests, die ausgeführt werden, werden auf der gezeigten Tabelle DYNAMIC_TEST
und einer Tabelle STATIC_TEST
durchgeführt. Die Tabelle STATIC_TEST
unterscheidet sich nur durch das Fehlen des Tabellenattribut DYNAMIC
von der Tabelle DYNAMIC_TEST
Alle Messungen laufen praktisch vollständig im Hauptspeicher ab. Der I/O-Buffer ist mit einem CACHE_SIZE
Wert von 60,000 Datenseiten a 8kb großzügig bemessen. Die maximale Tabellengröße liegt während des Tests bei jeweils rund 4.600 Datenseiten. Zusammen belegen sie also weniger als 10.000 Datenseiten, was nicht einmal 20% des I/O-Buffer beträgt. Die Testmaschine hat 1.5GB platz und kann zusätzlich zum I/O-Buffer der Datenbank etliche Daten im Betriebssystem Cache halten. Das Tool iostat
zeigt während des gut 10 Minuten andauernden Testlaufs eine I/O-Wait Rate von weniger als 1% im 5-Minuten Mittel.
Getestet wurde mit MaxDB 7.5.00.26 unter Suse 9.3.
Testverfahren
Zu Beginn des Tests werden beide Tabellen neu angelegt und mit 100.000 Datensätzen befüllt. Die Tabellen werden mit identischen Daten versorgt. Die Spalten a, b, c und d werden mit zufälligen, 50-100 Zeichen langen Strings belegt. Nach jeder großen Änderungsoperation, nach jedem Einzeltest werden die Tabellenstatistiken aus der Systemtabelle TABLESTATISTICS
abgelegt. Die erste Erfassung der Statistiken erfolgt nach der initialen Befüllung und wird in den Testergebnissen als “Ausgangslage” bezeichnet.
Im Testabschnitt “UPDATE 1” werden 100.000 UPDATE-Anweisungen auf den Tabellen ausgeführt. Es werden hierfür zufällige Datensätze ausgewählt und aktualisiert. Die verwendete UPDATE-Anweisung ist: UPDATE [DYNAMIC_TEST|STATIC_TEST] SET a = [Zufallswert, 0-100 Zeichen], d = [Zufallswert, 0-100 Zeichen] WHERE pk = [Zufallswert]
. Wie bei der Anlage der Daten werden die generierten UPDATE-Anweisungen in beiden Tabellen ausgeführt, d.h. es bleibt bei einem identischen Datenbestand in den Tabellen.
Nach der zufälligen Aktualisierung erfolgt eine sequentielle Aktualisierung aller Datensätze in beiden Tabellen. Die Aktualisierung erfolgt in Primärschlüsselreihenfolge. Es wird folgende Anweisung ausgeführt: UPDATE [DYNAMIC_TEST|STATIC_TEST] SET b = '', c = 'abc' WHERE pk = [aufsteigender Schlüsselwert]
. Dieser Schritt wird als “UPDATE 2” bezeichnet.
Nachdem mit insgesamt 200.000 Anweisungen jeder Datensatz in den Tabellen zwei mal verändert wurde, wird die Geschwindigkeit beim wahlfreien Zugriff über den Primärschlüssel gemessen. Erstmals werden nicht identische Zugriffe ausgeführt sondern für jede Tabelle 100.000 zufällige Primärschlüssel ermittelt. Durch die Menge sollte sich jedoch ein ähnliches, d.h. gleich zufälliges Zugriffsmuster ergeben. Im Mittel wird jeder Datensatz einmal abgefragt. Die Ergebnisse dieses Tests werden in der Auswertung unter “Random SELECT” angezeigt.
Der Effekt von Löschungen wird im Schritt “DELETE” vermessen. Insgesamt werden 20.000 Datensätze in aufsteigender Primärschlüsselreihenfolge gelöscht. Die Anzahl der Datensätze pro Datenseite schwankte nach der initialen Befüllung zwischen 10 und 25, nach dem Schritt “UPDATE 2” erhöht sich dieser Wert auf 20 bis 40 Datensätze pro Datenseite. Eine Löschung jedes 5. Datensatz dürfte damit jeden 4. bis 8. Datensatz pro Datenseite treffen. Der bereits vor der Löschung auf rund 50% gesunkene Belegungsgrad in STATIC_TABLE
wird unter einen Schwellenwert fallen und viele Ausgleichsoperationen nach sich führen.
Im letzten Test “INSERT” werden die soebend gelöschten Primärschlüsselwerte wieder eingefügt. In jede Tabelle werden also 20.000 neue Datensätze eingefügt:
INSERT INTO [DYNAMIC_TEST|STATIC_TEST] (pk, a, b) VALUES ([Primärschlüssel], 'abcdefghijklmdefghijklmdefghijklmdefghijklmdefghijklmdefghijklmdefghijklm', 'defghijklmdefghijklmdefghijklmdefghijklmdefghijklmdefghijklmdefghijklm')
.
Testergebnisse: Performanz
Die mit dem Attribut DYNAMIC
definierte Tabelle DYNAMIC_TEST
kann leichte Geschwindigkeitsvorteile von rund 10% bei Löschoperationen und spürbare 20% beim abschließenden INSERT-Test verbuchen. Fast keine Unterschiede sind bei der sequentiellen Aktualisierung “UPDATE 2” zu erkennen. Auch der minimale Vorteil beim “Random SELECT” ist nicht nennenswert. Bei mehreren Messungen schwankten alle relativen Prozentwerte zwischen STATIC_TEST
(mit Reorganisation zur Laufzeit) und DYNAMIC_TEST
(minimale Reorganisation) im Bereich von 2-5%. Damit läßt sich nur für DELETE- und INSERT-Anweisungen ein Trend feststellen.
STATIC_TEST | DYNAMIC_TEST | ||
UPDATE 2 | 67.46 s | 68.53 s | 101 % |
Random SELECT | 78.62 s | 77.11 s | 98 % |
DELETE | 15.04 s | 13.66 s | 90 % |
INSERT | 12.65 s | 10.01 s | 79 % |
Die Geschwindigkeitsvorteile werden jedoch mit einem erhebliche höheren Speicherplatzbedarf erkauft. Die mit dem Attribut DYNAMIC
versehene Tabelle sinkt am Ende des Test auf eine mittlere Belegung der Datenseiten von nur 45%, während die normale Tabelle einen guten Füllgrad von 85% erreicht. In absoluten Zahlen ausgedrückt belegt die Tabelle DYNAMIC_TEST
87% mehr Speicherplatz.
Fazit
Wenn Sie viel Speicherplatz zur Verfügung haben, ihre Datenbank weitgehend aus dem I/O-Buffer bedient werden kann und ihre Anwendung eine große Menge von Lösch- und Einfügeoperationen (DELETE, INSERT
) hat, dann ist DYNAMIC
interessant für Sie. Entsteht die Last vorwiegend durch Aktualisierungen (UPDATE
), dann sind wahrscheinlich nur geringe Geschwindigkeitsvorteile zu erwarten. Sollte es nicht möglich sein alle Daten im I/O-Buffer zu halten, dann besteht die Gefahr daß das Lesen der Datenseiten von der Platte zum dominierenden Faktor wird. DYNAMIC
kann zu einem sehr schlechten Belegungsgrad der Datenseiten führen und weit mehr langsame I/O-Operationen zum Lesen der gleichen Menge von Datensätzen hervorrufen als eine reorganisierte Tabelle.
Wie vorsichtig man mit diesen Beobachtungen umgehen muß, zeigte ein kleiner Dreh an den Parametern für den DELETE und INSERT Test. Löscht man jede 1. Zeile, also alles und füllt alles wieder auf, dann ist DYNAMIC
nicht mehr schneller und fällt negativ auf mit einer nur 20%-igen Belegung der Datenseiten.
Es ist also dringend anzuraten Tests mit der eigenen Applikation durchzuführen.
Testergebnisse: Speicherplatzbelegung
Ausgangslage |
|||
STATIC_TEST | DYNAMIC_TEST | ||
Root pno | 15668 | 105677 | 674 % |
Filetype | perm | perm | 0 % |
Used pages | 4618 | 4618 | 100 % |
Index pages | 14 | 14 | 100 % |
Leaf pages | 4604 | 4604 | 100 % |
Index levels | 2 | 2 | 100 % |
Space used in all pages (%) | 91 | 91 | 100 % |
Space used in root page (%) | 3 | 3 | 100 % |
Space used in index pages (%) | 87 | 87 | 100 % |
Space used in index pages (%) min | 3 | 3 | 100 % |
Space used in index pages (%) max | 99 | 99 | 100 % |
Space used in leaf pages (%) | 91 | 91 | 100 % |
Space used in leaf pages (%) min | 38 | 38 | 100 % |
Space used in leaf pages (%) max | 95 | 95 | 100 % |
Rows | 100000 | 100000 | 100 % |
Avg rows per page | 21 | 21 | 100 % |
Min rows per page | 9 | 9 | 100 % |
Max rows per page | 23 | 23 | 100 % |
Avg row length | 339 | 339 | 100 % |
Min row length | 246 | 246 | 100 % |
Max row length | 436 | 436 | 100 % |
Avg key length | 4 | 4 | 100 % |
Min key length | 2 | 2 | 100 % |
Max key length | 5 | 5 | 100 % |
Avg separator length | 4 | 4 | 100 % |
Min separator length | 3 | 3 | 100 % |
Max separator length | 5 | 5 | 100 % |
Defined LONG columns | 0 | 0 | 0 % |
Avg LONG column length | 0 | 0 | 0 % |
Min LONG column length | 0 | 0 | 0 % |
Max LONG column length | 0 | 0 | 0 % |
LONG column pages | 0 | 0 | 0 % |
Avg pages per LONG column | 0 | 0 | 0 % |
Min pages per LONG column | 0 | 0 | 0 % |
Max pages per LONG column | 0 | 0 | 0 % |
UPDATE 1 |
|||
STATIC_TEST | DYNAMIC_TEST | ||
Root pno | 15668 | 105677 | 674 % |
Filetype | perm | perm | 0 % |
Used pages | 4618 | 4618 | 100 % |
Index pages | 14 | 14 | 100 % |
Leaf pages | 4604 | 4604 | 100 % |
Index levels | 2 | 2 | 100 % |
Space used in all pages (%) | 83 | 83 | 100 % |
Space used in root page (%) | 3 | 3 | 100 % |
Space used in index pages (%) | 87 | 87 | 100 % |
Space used in index pages (%) min | 3 | 3 | 100 % |
Space used in index pages (%) max | 99 | 99 | 100 % |
Space used in leaf pages (%) | 83 | 83 | 100 % |
Space used in leaf pages (%) min | 58 | 36 | 62 % |
Space used in leaf pages (%) max | 93 | 93 | 100 % |
Rows | 100000 | 100000 | 100 % |
Avg rows per page | 21 | 21 | 100 % |
Min rows per page | 15 | 9 | 60 % |
Max rows per page | 23 | 23 | 100 % |
Avg row length | 307 | 307 | 100 % |
Min row length | 147 | 147 | 100 % |
Max row length | 437 | 437 | 100 % |
Avg key length | 4 | 4 | 100 % |
Min key length | 2 | 2 | 100 % |
Max key length | 5 | 5 | 100 % |
Avg separator length | 4 | 4 | 100 % |
Min separator length | 3 | 3 | 100 % |
Max separator length | 5 | 5 | 100 % |
Defined LONG columns | 0 | 0 | 0 % |
Avg LONG column length | 0 | 0 | 0 % |
Min LONG column length | 0 | 0 | 0 % |
Max LONG column length | 0 | 0 | 0 % |
LONG column pages | 0 | 0 | 0 % |
Avg pages per LONG column | 0 | 0 | 0 % |
Min pages per LONG column | 0 | 0 | 0 % |
Max pages per LONG column | 0 | 0 | 0 % |
UPDATE 2 |
|||
STATIC_TEST | DYNAMIC_TEST | ||
Root pno | 15668 | 105677 | 674 % |
Filetype | perm | perm | 0 % |
Used pages | 3451 | 4618 | 133 % |
Index pages | 13 | 14 | 107 % |
Leaf pages | 3438 | 4604 | 133 % |
Index levels | 2 | 2 | 100 % |
Space used in all pages (%) | 59 | 44 | 74 % |
Space used in root page (%) | 3 | 3 | 100 % |
Space used in index pages (%) | 70 | 87 | 124 % |
Space used in index pages (%) min | 3 | 3 | 100 % |
Space used in index pages (%) max | 97 | 99 | 102 % |
Space used in leaf pages (%) | 59 | 44 | 74 % |
Space used in leaf pages (%) min | 50 | 20 | 40 % |
Space used in leaf pages (%) max | 78 | 52 | 66 % |
Rows | 100000 | 100000 | 100 % |
Avg rows per page | 29 | 21 | 72 % |
Min rows per page | 21 | 9 | 42 % |
Max rows per page | 44 | 23 | 52 % |
Avg row length | 161 | 161 | 99 % |
Min row length | 44 | 44 | 100 % |
Max row length | 245 | 245 | 100 % |
Avg key length | 4 | 4 | 100 % |
Min key length | 2 | 2 | 100 % |
Max key length | 5 | 5 | 100 % |
Avg separator length | 4 | 4 | 100 % |
Min separator length | 3 | 3 | 100 % |
Max separator length | 5 | 5 | 100 % |
Defined LONG columns | 0 | 0 | 0 % |
Avg LONG column length | 0 | 0 | 0 % |
Min LONG column length | 0 | 0 | 0 % |
Max LONG column length | 0 | 0 | 0 % |
LONG column pages | 0 | 0 | 0 % |
Avg pages per LONG column | 0 | 0 | 0 % |
Min pages per LONG column | 0 | 0 | 0 % |
Max pages per LONG column | 0 | 0 | 0 % |
DELETE |
|||
STATIC_TEST | DYNAMIC_TEST | ||
Root pno | 15668 | 105677 | 674 % |
Filetype | perm | perm | 0 % |
Used pages | 2463 | 4618 | 187 % |
Index pages | 13 | 14 | 107 % |
Leaf pages | 2450 | 4604 | 187 % |
Index levels | 2 | 2 | 100 % |
Space used in all pages (%) | 66 | 35 | 53 % |
Space used in root page (%) | 3 | 3 | 100 % |
Space used in index pages (%) | 50 | 87 | 174 % |
Space used in index pages (%) min | 3 | 3 | 100 % |
Space used in index pages (%) max | 69 | 99 | 143 % |
Space used in leaf pages (%) | 66 | 35 | 53 % |
Space used in leaf pages (%) min | 50 | 17 | 34 % |
Space used in leaf pages (%) max | 92 | 44 | 47 % |
Rows | 80000 | 80000 | 100 % |
Avg rows per page | 32 | 17 | 53 % |
Min rows per page | 22 | 8 | 36 % |
Max rows per page | 48 | 19 | 39 % |
Avg row length | 161 | 161 | 99 % |
Min row length | 45 | 45 | 100 % |
Max row length | 245 | 245 | 100 % |
Avg key length | 4 | 4 | 100 % |
Min key length | 3 | 3 | 100 % |
Max key length | 5 | 5 | 100 % |
Avg separator length | 4 | 4 | 100 % |
Min separator length | 3 | 3 | 100 % |
Max separator length | 5 | 5 | 100 % |
Defined LONG columns | 0 | 0 | 0 % |
Avg LONG column length | 0 | 0 | 0 % |
Min LONG column length | 0 | 0 | 0 % |
Max LONG column length | 0 | 0 | 0 % |
LONG column pages | 0 | 0 | 0 % |
Avg pages per LONG column | 0 | 0 | 0 % |
Min pages per LONG column | 0 | 0 | 0 % |
Max pages per LONG column | 0 | 0 | 0 % |
INSERT |
|||
STATIC_TEST | DYNAMIC_TEST | ||
Root pno | 15668 | 105677 | 674 % |
Filetype | perm | perm | 0 % |
Used pages | 2465 | 4618 | 187 % |
Index pages | 13 | 14 | 107 % |
Leaf pages | 2452 | 4604 | 187 % |
Index levels | 2 | 2 | 100 % |
Space used in all pages (%) | 85 | 45 | 52 % |
Space used in root page (%) | 3 | 3 | 100 % |
Space used in index pages (%) | 50 | 87 | 174 % |
Space used in index pages (%) min | 3 | 3 | 100 % |
Space used in index pages (%) max | 69 | 99 | 143 % |
Space used in leaf pages (%) | 85 | 45 | 52 % |
Space used in leaf pages (%) min | 48 | 20 | 41 % |
Space used in leaf pages (%) max | 100 | 54 | 54 % |
Rows | 100000 | 100000 | 100 % |
Avg rows per page | 40 | 21 | 52 % |
Min rows per page | 23 | 9 | 39 % |
Max rows per page | 52 | 23 | 44 % |
Avg row length | 166 | 166 | 100 % |
Min row length | 45 | 45 | 100 % |
Max row length | 245 | 245 | 100 % |
Avg key length | 4 | 4 | 100 % |
Min key length | 2 | 2 | 100 % |
Max key length | 5 | 5 | 100 % |
Avg separator length | 4 | 4 | 100 % |
Min separator length | 3 | 3 | 100 % |
Max separator length | 5 | 5 | 100 % |
Defined LONG columns | 0 | 0 | 0 % |
Avg LONG column length | 0 | 0 | 0 % |
Min LONG column length | 0 | 0 | 0 % |
Max LONG column length | 0 | 0 | 0 % |
LONG column pages | 0 | 0 | 0 % |
Avg pages per LONG column | 0 | 0 | 0 % |
Min pages per LONG column | 0 | 0 | 0 % |
Max pages per LONG column | 0 | 0 | 0 % |