Oracle Database Indexing Best Practices, Teil 3 - DOAG
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Oracle Database Indexing Best Practices, Teil 3 Randolf Geist, Unabhängiger Berater Auch wenn die Version 19c der Oracle-Datenbank die automatische Indizierung als neues Feature mit sich bringt, wird die Indizierung auch in Zukunft ein Dauerbrenner-Thema bleiben – auch schon aufgrund der Lizenzpolitik seitens Oracle, die das neue Feature in naher Zukunft nur einem Bruchteil der Kunden zukommen lassen wird. Daher soll dieser Artikel mit einigen Mythen im Bereich Indizierung aufräumen und hilfreiche Tipps und Tricks beschreiben, die helfen, besser zu verstehen, was im Bereich Indizierung sinnvoll und nützlich ist. Im dritten und letzten Teil dieser Reihe schauen wir weiter auf Mehrspalten-Indizes und darauf, was es diesbezüglich noch weiter zu beachten gilt. Red Stack Magazin 01/2021 29
Datenbank Index-Komprimierung alter index CONC_INDEX1_CODE_ID rebuild compress 1; Durch das Voranstellen von Spalten mit we- SQL> SELECT index_name, blevel, leaf_blocks, clustering_factor nigen Ausprägungen kann noch ein anderes 2 from user_indexes Feature zum Einsatz kommen, das wahr- 3 where index_name = 'CONC_INDEX1_CODE_ID' or index_name = 'CONC_ scheinlich zu den am wenigsten genutzten, INDEX1_ID_CODE'; INDEX_NAME BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR kostenlosen (Enterprise Edition) Features -------------------- -------- ------------ ----------------- der Datenbank gehört: die sogenannte In- CONC_INDEX1_CODE_ID 2 2924 1000000 dex Compression. Diese gibt es schon seit CONC_INDEX1_ID_CODE 2 3306 1000000 der Version 8 von Oracle als „Basic Index Compression“, die keine weitere Lizenz über Listing 1: Kompressionseffekt nur mit Angabe der führenden Spalte die Enterprise Edition hinaus benötigt. Seit der Version 12c gibt es zusätzlich eine „Ad- vanced Index Compression“, die eine „Ad- kann trotzdem die Größe einer Index- ten für die Kompression verwendet, kann vanced Compression“-Lizenz voraussetzt. Struktur signifikant verkleinern. sich der Effekt ins Gegenteil verkehren: Bei Die „Basic Index Compression“ ersetzt Der maßgebliche Unterschied zwi- sich entsprechend wenig wiederholenden in den Index Leaf Blocks führende Werte, schen „Basic Index Compression“ und der Werten wird so viel Platz für Platzhalter und die sich wiederholen, mit einem kürzeren kostenpflichtigen „Advanced Index Com- Wertspeicherung verbraucht, sodass der Platzhalter/Symbol, und speichert den pression“ besteht darin, dass man bei der Index größer anstatt kleiner werden kann. sich wiederholenden Wert nur einmalig „Basic“-Variante die Anzahl der sinnvoll zu Insofern sollte man sich hier gut überle- im Block ab. Je größer also der Längenun- komprimierenden führenden Spalten selbst gen, welche Anzahl von Spalten Sinn ergib. terschied zwischen Wert und Platzhalter, bestimmen muss und dies von Oracle be- Oracle kann diesen Wert tatsächlich auch desto effektiver der „Kompressionsfak- dingungslos umgesetzt wird – gibt man kei- selbst ermitteln, leider ist dies aber um- tor“. Durch diese simple Implementie- nen Wert an, komprimiert Oracle alle Spal- ständlich: Mithilfe der Ausführung des Be- rung erzeugt diese Art des Platzsparens ten (außer bei UNIQUE-Indizes, hier werden fehls ANALYZE INDEX … VALIDATE STRUC- so gut wie keinen messbaren Overhead alle bis auf die letzte Spalte komprimiert). TURE wird der spezielle View INDEX_STATS beim Suchen und Pflegen des Index und Wird hier eine unpassende Anzahl von Spal- befüllt – und dieser beinhaltet neben vielen select * from conc_index1 a where code = 2 and id2 = 4243; ID CODE ID2 FILLE -------- ---------- ---------- ----- 4242 2 4243 x 4242 2 4243 x ----------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 38 | 593 (1)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| CONC_INDEX1 | 2 | 38 | 593 (1)| 00:00:01 | |* 2 | INDEX RANGE SCAN | CONC_INDEX1_CODE_ID | 2 | | 591 (1)| 00:00:01 | ----------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("CODE"=2 AND "ID2"=4243) filter("ID2"=4243) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 596 consistent gets 0 physical reads 0 redo size 565 bytes sent via SQL*Net to client 371 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 2 rows processed Listing 2: Kompressionseffekt mit Zugriff auf die erste und dritte Spalte 30 www.aoug.at • www.doag.org • www.soug.ch
CREATE INDEX conc_index1_id_id2 ON conc_index1 (id, id2); SQL> SELECT index_name, blevel, leaf_blocks, clustering_factor 2 from user_indexes 3 where table_name = 'CONC_INDEX1' 4 ; INDEX_NAME BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR -------------------- ---------- ----------- ----------------- CONC_INDEX1_ID_ID2 2 2919 1000000 SQL> select * from conc_index1 a where id = 10000 and id2 = 10001; ---------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 38 | 5 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| CONC_INDEX1 | 2 | 38 | 5 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | CONC_INDEX1_ID_ID2 | 2 | | 3 (0)| 00:00:01 | ---------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID"=10000 AND "ID2"=10001) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 6 consistent gets 0 physical reads 0 redo size 563 bytes sent via SQL*Net to client 372 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 2 rows processed Listing 3: Index-Erzeugung und Ausführung einer Beispiel-Abfrage auf beide Spalten per Gleichheitsvergleich mittels SQL*Plus AUTOTRACE anderen Statistiken über den untersuch- welche führenden Werte sich für die In unserem konkreten Fall ist die Lage ten Index auch die Spalte OPT_COMPR_ Kompression eignen können – ist also allerdings auch ohne das Ausführen des COUNT, welche die von Oracle ermittelte in der Lage, das Verfahren flexibel an- ANALYZE INDEX VALIDATE STRUCTURE- optimale Anzahl von führenden Spalten für zupassen und damit unterschiedliche Befehls relativ offensichtlich – nur die die „Basic Index Compression“ darstellt. Einstellungen pro Block vornehmen zu führende CODE-Spalte wiederholt sich si- Leider wird diese Information nur im können, je nachdem, wie die tatsäch- gnifikant, die weiteren ID- und ID2-Spal- OFFLINE-Modus von VALIDATE STRUC- lichen Daten aussehen. Dies erlaubt ten haben viele Ausprägungen und wie- TURE ermittelt – und dieser wiederum dann auch, den Kompressionseffekt derholen sich wenig, wenn auch pro Wert sperrt den Index gegen Veränderungen über die gesamte Index-Struktur gese- zwei Mal. Insofern bekommen wir wahr- während der Ausführung und ist somit hen zu maximieren. scheinlich mit entweder nur der führen- auf aktiven Datenbanken nicht wirklich Inzwischen, seit der Version 12.2, gibt den Spalte oder allen dreien den besten sinnvoll einsetzbar – wohl dem, der eine es auch noch eine „Advanced High In- Kompressionseffekt – hier in Listing 1 ein Kopie der Datenbank vorliegen hat, wo dex Compression“, die ein ganz anderes Beispiel dafür, was passiert, wenn ich nur der Befehl ohne Auswirkungen ausge- Konzept verfolgt und mit einem tatsäch- die führende Spalte angebe. führt werden kann. lichen Kompressionsverfahren nochmal Der Index ist jetzt ca. 10% kleiner ge- Die „Advanced Index Compression“ ganz andere Kompressionsfaktoren für worden – mehr ist durch die Kürze der löst dieses Problem auf sehr elegante B*Tree-Indizes erreichen kann, wenn CODE-Spalte nicht drin, allerdings spa- Art und Weise – hier evaluiert Oracle auch mit deutlich mehr CPU-Overhead ren wir uns ohne weitere Kosten dadurch für jeden Index Leaf Block automatisch, verbunden. eben 10% an Platz und bei entsprechen- Red Stack Magazin 01/2021 31
Datenbank select * from conc_index1 a where id between 0 and 250000 and id2 = 10001; --------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 19 | 912 (3)| 00:00:01 | |* 1 | TABLE ACCESS FULL | CONC_INDEX1 | 1 | 19 | 912 (3)| 00:00:01 | --------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("ID2"=10001 AND "ID"=0) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 3289 consistent gets 0 physical reads 0 redo size 555 bytes sent via SQL*Net to client 371 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 2 rows processed Listing 4: Ergebnis bei Bereichssuche auf der ID-Spalte über die Hälfte des vorhandenen Werteraums select /*+ index(a) */ * from conc_index1 a where id between 0 and 250000 and id2 = 10001; ----------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| ----------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 19 | 1472 (1)| | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| CONC_INDEX1 | 1 | 19 | 1472 (1)| |* 2 | INDEX RANGE SCAN | CONC_INDEX1_ID_ID2 | 1 | | 1471 (1)| ----------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID">=0 AND "ID2"=10001 AND "ID"
den Operationen auch 10% an Arbeit – in SKIP SCAN-Zugriffen Abfragen verar- denen Werteraums (Werteraum insgesamt Listing 2 dargestellt am Beispiel des nicht beiten kann. von 0 bis 499.999, Suche von 0 bis 250.000), mehr so effizienten Zugriffs auf die erste • Das Voranstellen dieser Spalten mit we- kann dies passieren (siehe Listing 4). und dritte Spalte. nig Ausprägungen erlaubt zusätzlich Hier wird nicht mehr der Index vom Die Operation benötigt jetzt also auch noch die bessere Nutzung der „Index Optimizer herangezogen, sondern statt- 10% weniger logisches I/O, da die Index- Compression“, die bei der „Enterprise dessen ein „Full Table Scan“ durchgeführt Struktur durch die Kompression kompak- Edition“ von Oracle als „Basic Index – eine ineffiziente Operation, um zwei Zei- ter geworden ist. Compression“ kostenfrei enthalten ist len aus einer Million zu identifizieren. Er- Grundsätzlich kann man sich also fol- und sowohl den Platzbedarf verringern zwingen wir den Index-Zugriff per Hint, gende Hinweise für die Reihenfolge von als auch die Effizienz steigern kann. wird auch klar, warum – wie sich in Listing Spalten in B*Tree-Indizes merken: 5 ablesen lässt. Durch Abfrage auf die Hälfte des Wer- • Solange man alle Spalten des Index bei Mehrspalten-Indizes und teraums der führenden ID-Spalte kann der Suche angibt, spielt die Reihenfol- Bereichsvergleiche nicht mehr effizient auf die ID2-Spalte ge der Spalten für die Effizienz des Zu- innerhalb der Index-Struktur zugegriffen griffs keine Rolle. Abschließend noch ein Beispiel dafür, werden. Grund dafür ist der gleiche wie • Soll der Index auch für andere Zugriffe was passiert, wenn mit Bereichsverglei- oben; er ergibt sich aus der Vorsortierung verwendet werden können, sollten die chen gearbeitet wird. Dazu erzeuge ich der Werte in der Index-Struktur – effek- Spalten an den Anfang gestellt werden, einen neuen Index auf der Tabelle, dies- tiv wird also der halbe Index durchsucht, die den effizientesten Zugriff erlauben – mal nur auf den Spalten ID und ID2. So- darum sind die berechneten Kosten auch bei denen also die Index-Struktur am ef- lange ich mit einfachen Werten suche, höher als beim „Full Table Scan“ und die fizientesten navigiert werden kann ohne funktioniert der Index-Zugriff effizient – logischen I/Os entsprechen ungefähr Überspringen von Spalten oder Bereichs- hier wie in Listing 3 dargestellt die Index- der Hälfte der Index-Größe (2.919 Leaf abfragen (dazu gleich noch mehr). Erzeugung sowie die Ausführung einer Blocks, 1.467 logische I/Os). Oracle zeigt • Die Einsatzmöglichkeiten eines Index Beispiel-Abfrage auf beide Spalten per das auch wieder in der „Predicate Infor- können möglicherweise erhöht wer- Gleichheitsvergleich mittels SQL*Plus mation“ an – die Suchbedingung auf ID2 den, wenn man Spalten mit wenig Aus- AUTOTRACE. wird als FILTER aufgeführt. prägungen an den Anfang des Index Mache ich aber eine Bereichssuche auf Möchte ich also die gezeigte Abfrage ef- stellt, da Oracle dann mittels INDEX der ID-Spalte über die Hälfte des vorhan- fizienter gestalten, muss ich die Reihenfol- CREATE INDEX conc_index1_id2_id ON conc_index1 (id2, id); SQL> select * from conc_index1 a where id = 10000 and id2 = 10001; ---------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 38 | 5 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| CONC_INDEX1 | 2 | 38 | 5 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | CONC_INDEX1_ID2_ID | 2 | | 3 (0)| 00:00:01 | ---------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID2"=10001 AND "ID"=10000) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 6 consistent gets 0 physical reads 0 redo size 563 bytes sent via SQL*Net to client 372 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 2 rows processed Listing 6: Abfrage mit ID2 als führender Spalte Red Stack Magazin 01/2021 33
Datenbank select * from conc_index1 where id between 0 and 250000 and id2 = 10001; ---------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 19 | 4 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| CONC_INDEX1 | 1 | 19 | 4 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | CONC_INDEX1_ID2_ID | 1 | | 3 (0)| 00:00:01 | ---------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID2"=10001 AND "ID">=0 AND "ID"
Sie können auch lesen