Logo Oracle Deutschland   DBA Community  -  Oktober 2014
Advanced Index Compression in Oracle Database 12c (12.1.0.2)
von Ulrike Schwinn, Oracle Deutschland B.V. & Co. KG

Komprimierung im Index findet seit jeher in den sogenannten Bitmap Indizes statt, die speziell in Warehouse-Anwendungen bei Spalten mit geringer Kardinalität zum Einsatz kommen. Wegen des speziellen Locking-Verhaltens bietet die Verwendung von Bitmap Indizes besonders bei lesenden Zugriffen Vorteile. Weniger bekannt war lange Zeit die Tatsache, dass auch "normale" B*Tree Indizes komprimiert werden können - im sogenannten Key Compression Index. Im Gegensatz zum Bitmap Index ist kein spezielles Locking erforderlich. Das Prinzip der Index Key Compression beruht dabei auf der Eliminierung von sich wiederholenden Schlüssel-Werten (auch prefix genannt) eines nonunique single column- oder eines unique multicolumn- Index. Die sogenannte Index Key Compression (auch prefix compression) ist bereits seit Oracle 9i verfügbar und steht für B*Tree Indizes und IOTs (Index Organized Table) zur Verfügung. Wer mehr dazu erfahren möchte, kann den Tipp Indizes in der Datenbank: Monitoring und Komprimierung (Index Key Compression) dazu konsultieren. Allerdings ist der Einsatz von Index Key Compression nicht immer sinnvoll. Beispielsweise muss bei einem nonunique single column Schlüssel zusätzlich die ROWID Information genutzt werden, um den Schlüssel eindeutig zu machen. Dies kann zu einem gewissen zusätzlichen Overhead führen.

Im aktuellen Datenbank Release 12.1.0.2 gibt es endlich eine Lösung für solche Kandidaten - die Advanced Index Compression. Diese Art der Komprimierung kann eine bessere Compression Ratio liefern und erfordert im Gegensatz zur Prefix Compression keine Angabe der Präfixlänge (Anzahl der Spalten, die komprimiert wird). Die Advanced Index Compression ist auf Blockebene implementiert. Ist ein Block voll, wird automatisch komprimiert, falls dadurch Platz für weitere Zeilen geschaffen werden kann. Zusätzlich ist der gleichzeitige volle Zugriff auf den Index wie beim Prefix Index gewährleistet.

Um ein Beispiel für eine sinnvolle Verwendung zu geben, wird im folgenden die Testtabelle CUSTOMER_TEST (55500 Zeilen) verwendet - eine Kopie der SH.CUSTOMER Tabelle. Geändert wurde nur der Inhalt der Spalte CUST_POSTAL_CODE. CUST_POSTAL_CODE enthält nun 35502 unterschiedliche/eindeutige Werte und ca. ein Drittel (20000) sich wiederholende Werte. Überprüfen wir kurz die Umgebung.

SQL> SELECT count(*) FROM customer_test;

  COUNT(*)
----------
     55500

SQL> SELECT column_name, num_distinct FROM user_tab_columns WHERE table_name='CUSTOMER_TEST';

COLUMN_NAME                    NUM_DISTINCT
------------------------------ ------------
CUST_ID                               55500
CUST_FIRST_NAME                        1300
CUST_LAST_NAME                          908
CUST_GENDER                               2
...
CUST_POSTAL_CODE                      35502

SQL> SELECT cust_postal_code, count(cust_postal_code), count(*) 
     FROM customer_test GROUP BY cust_postal_code;

CUST_POSTA COUNT(CUST_POSTAL_CODE)   COUNT(*)
---------- ----------------------- ----------
11111                        19999      19999
11112                            1          1
11113                            1          1
11114                            1          1
11115                            1          1
11116                            1          1
...
CUST_POSTA COUNT(CUST_POSTAL_CODE)   COUNT(*)
---------- ----------------------- ----------
46608                            1          1
46609                            1          1
46610                            1          1
46611                            1          1
46612                            1          1

35502 rows selected.
Im ersten Schritt wird ein "normaler" B*Tree Index erzeugt und die Anzahl der Leaf Blöcke ausgegeben.
SQL>  CREATE INDEX b_cust_postal ON customer_test(cust_postal_code);
Index created.

SQL> SELECT leaf_blocks, blevel FROM user_indexes WHERE index_name LIKE 'B%';

LEAF_BLOCKS     BLEVEL
----------- ----------
        132          1
Um vorab zu testen, ob die neue Advanced Index Compression von Vorteil ist, kann eine Erweiterung des Compression Advisors verwendet werden. Als Eingabeparameter sind Werte für den Test Tablespace (USERS), das Schema (SH), den Index (B_CUST_POSTAL), den Objekttyp (DBMS_COMPRESSION.OBJTYPE_INDEX) und den neuen Compression Typ (DBMS_COMPRESSION.COMP_INDEX_ADVANCED_LOW) notwendig. Das folgende Beispiel Skript zeigt eine Anwendung.
set serveroutput on
DECLARE
l_blkcnt_cmp   PLS_INTEGER;
l_blkcnt_uncmp PLS_INTEGER;
l_row_cmp      PLS_INTEGER;
l_row_uncmp    PLS_INTEGER;
l_cmp_ratio    PLS_INTEGER;
l_comptype_str VARCHAR2(100);
BEGIN
dbms_compression.get_compression_ratio (
  scratchtbsname => 'USERS',
  ownname        => 'SH',
  objname        => 'B_CUST_POSTAL',
  subobjname     => '',
  comptype       => dbms_compression.COMP_INDEX_ADVANCED_LOW,
  blkcnt_cmp     => l_blkcnt_cmp,
  blkcnt_uncmp   => l_blkcnt_uncmp,
  row_cmp        => l_row_cmp,
  row_uncmp      => l_row_uncmp,
  cmp_ratio      => l_cmp_ratio,
  comptype_str   => l_comptype_str,
  subset_numrows => dbms_compression.COMP_RATIO_MINROWS,
  objtype        => dbms_compression.OBJTYPE_INDEX);

DBMS_OUTPUT.PUT_LINE('Block count compressed = ' || l_blkcnt_cmp);
DBMS_OUTPUT.PUT_LINE('Block count uncompressed = ' || l_blkcnt_uncmp);
DBMS_OUTPUT.PUT_LINE('Row count per block compressed = ' || l_row_cmp);
DBMS_OUTPUT.PUT_LINE('Row count per block uncompressed = ' || l_row_uncmp);
DBMS_OUTPUT.PUT_LINE('Compression type = ' || l_comptype_str);
END;
/
Block count compressed = 116
Block count uncompressed = 133
Row count per block compressed = 478
Row count per block uncompressed = 417
Compression type = "Compress Advanced Low"
Wie man leicht erkennen kann, werden nur noch 116 Leaf Blöcke (Block count compressed) geschätzt. Nun überprüfen wir das Ganze mit der tatsächlichen Objekt Compression. Die neue Syntax lautet COMPRESS ADVANCED LOW und kann im ALTER und CREATE INDEX Kommando verwendet werden. Es gibt übrigens (noch) kein COMPRESS ADVANCED HIGH, auch wenn diese zusätzliche Syntaxform nahe liegt.
SQL> ALTER INDEX b_cust_postal REBUILD COMPRESS ADVANCED LOW;
Index altered.

-- Ueberpruefung
SQL> SELECT index_name, compression FROM user_indexes;
INDEX_NAME                     COMPRESSION
------------------------------ -------------
B_CUST_POSTAL                  ADVANCED LOW
CHANNELS_PK                    DISABLED
...

SQL> SELECT leaf_blocks, blevel FROM user_indexes WHERE index_name LIKE 'B%';

LEAF_BLOCKS     BLEVEL
----------- ----------
        115          1
Die Schätzung war gut. Der Index hat jetzt nur noch 115 Leaf Blöcke. Um das Beispiel zu vervollständigen, ändern wir die Art der Komprimierung in Key Compression und überprüfen erneut die Anzahl der Leaf Blöcke.
SQL> ALTER INDEX b_cust_postal REBUILD COMPRESS 1;
Index altered.

-- Ueberpruefung
SQL> SELECT index_name, compression FROM user_indexes;
INDEX_NAME                     COMPRESSION
------------------------------ -------------
B_CUST_POSTAL                  ENABLED
CHANNELS_PK                    DISABLED
...
SQL> SELECT leaf_blocks, blevel FROM user_indexes WHERE index_name LIKE 'B%';

LEAF_BLOCKS     BLEVEL
----------- ----------
        145          1
Das Ergebnis zeigt die Verwendung von 145 Leaf Blöcke - die Anzahl ist sogar höher als beim normalen B*Tree Index.

Hinweis: Das Beispiel soll nur als kleiner Einstieg in die Technologie dienen. Diese Art der Komprimierung kann natürlich auch in Multicolumn Indizes bzw. bei partitionierten Indizes Verwendung finden.

Lizenzierung: Bitte beachten Sie, dass die Nutzung der COMPRESS ADVANCED LOW Syntax die Lizenzierung der Advanced Compression Option erfordert.

Zurück zur Community-Seite