Mittwoch, Oktober 24, 2012

Bitmap Indizes und Partitions-Compression

Im Fall des folgenden Problems wundere ich mich, dass mir das erst jetzt aufgefallen ist - aber vielleicht habe ich frühere Begegnungen auch einfach vergessen. Es handelt sich um eine jener kleinen Asymmetrien, bei denen ich mir die Frage stelle, ob es dafür eine inhaltliche Begründung gibt: das nachträgliche Komprimieren einzelner Partitionen von Tabellen mit lokalen B*Tree Indizes ist eine ganz harmlose Operation. Kommen aber bitmap Indizes ins Spiel, wird der Vorgang komplizierter. Dazu ein Test.

Fall 1: nachträgliche Komprimierung einer Partition mit B*Tree Index


-- 11.1.0.7
-- Anlage einer List-partitionierten Tabelle mit zwei Spalten
drop table list_part;
create table list_part (
    start_date date
  , col1 number
)
partition by list (start_date)
(
    partition P201201 values (to_date('01.01.2012', 'dd.mm.yyyy'))
  , partition P201202 values (to_date('01.02.2012', 'dd.mm.yyyy'))
  , partition P201203 values (to_date('01.03.2012', 'dd.mm.yyyy'))
  , partition P201204 values (to_date('01.04.2012', 'dd.mm.yyyy'))
  , partition P201205 values (to_date('01.05.2012', 'dd.mm.yyyy'))
  , partition P201206 values (to_date('01.06.2012', 'dd.mm.yyyy'))
  , partition P201207 values (to_date('01.07.2012', 'dd.mm.yyyy'))
  , partition P201208 values (to_date('01.08.2012', 'dd.mm.yyyy'))
  , partition P201209 values (to_date('01.09.2012', 'dd.mm.yyyy'))
  , partition P201210 values (to_date('01.10.2012', 'dd.mm.yyyy'))
);

insert into list_part(start_date, col1) 
values (to_date('01.01.2012', 'dd.mm.yyyy'), 1);

-- Anlage eines lokalen B*Tree Index
create index list_part_ix_local on list_part (col1) local;

Im Lauf der Zeit wird mir nun klar, dass ich die älteren Partitionen dieser Tabelle gerne komprimieren würde:

alter table list_part modify partition P201201 compress;
--> Tabelle wurde geändert.
alter table list_part move partition P201201;
--> Tabelle wurde geändert.

select partition_name
     , status
  from user_ind_partitions
 where index_name = 'LIST_PART_IX_LOCAL'
/

PARTITION_NAME                 STATUS
------------------------------ --------
P201201                        UNUSABLE
P201202                        USABLE
P201203                        USABLE
P201204                        USABLE
P201205                        USABLE
P201206                        USABLE
P201207                        USABLE
P201208                        USABLE
P201209                        USABLE
P201210                        USABLE

Die nachträgliche Komprimierung der Einzelpartition funktioniert also ohne Probleme. Der lokale Index wird durch die physikalische Reorganisation der Tabelle aber natürlich unbenutzbar und muss neu aufgebaut werden.

Fall 2: nachträgliche Komprimierung einer Partition mit bitmap Index


Nun zum bitmap Index:

-- 11.1.0.7
-- Anlage einer List-partitionierten Tabelle mit zwei Spalten
drop table list_part;
create table list_part (
    start_date date
  , col1 number
)
partition by list (start_date)
(
    partition P201201 values (to_date('01.01.2012', 'dd.mm.yyyy'))
  , partition P201202 values (to_date('01.02.2012', 'dd.mm.yyyy'))
  , partition P201203 values (to_date('01.03.2012', 'dd.mm.yyyy'))
  , partition P201204 values (to_date('01.04.2012', 'dd.mm.yyyy'))
  , partition P201205 values (to_date('01.05.2012', 'dd.mm.yyyy'))
  , partition P201206 values (to_date('01.06.2012', 'dd.mm.yyyy'))
  , partition P201207 values (to_date('01.07.2012', 'dd.mm.yyyy'))
  , partition P201208 values (to_date('01.08.2012', 'dd.mm.yyyy'))
  , partition P201209 values (to_date('01.09.2012', 'dd.mm.yyyy'))
  , partition P201210 values (to_date('01.10.2012', 'dd.mm.yyyy'))
);

insert into list_part(start_date, col1) 
values (to_date('01.01.2012', 'dd.mm.yyyy'), 1);

-- Anlage eines lokalen bitmap Index
-- wobei der Versuch, auf einer partitionierten Tabelle einen globalen bitmap Index 
-- zu erzeugen, mit der wunderbar klaren Fehlermeldung "ORA-25122
-- Bei partitionierten Tabellen sind nur LOCAL-Bitmap-Indizes zulässig"
-- beantwortet wird.
create bitmap index list_part_bix_local on list_part (col1) local;

Und auch für diesen Fall will ich die erste Partition nachträglich komprimieren:

alter table list_part modify partition P201201 compress;
--> Tabelle wurde geändert.
alter table list_part move partition P201201;

FEHLER in Zeile 1:
ORA-14646: Der angegebene Vorgang zur Änderungen einer Tabelle
der eine Komprimierung umfasst, kann nicht ausgeführt werden,
wenn verwendbare Bitmap-Indizes vorhanden sind.

Na gut, dann mache ich die Index-Partition eben UNUSABLE:

alter index list_part_bix_local modify partition P201201 unusable;
--> Index wurde geändert.
alter table list_part move partition P201201;

FEHLER in Zeile 1:
ORA-14646: Der angegebene Vorgang zur Änderungen einer Tabelle
der eine Komprimierung umfasst, kann nicht ausgeführt werden,
wenn verwendbare Bitmap-Indizes vorhanden sind.

Das hilft also nicht. Tatsächlich muss in diesem Fall nicht nur die lokale Partition, sondern der gesamte Index UNUSABLE gesetzt und später wieder per REBUILD neu aufgebaut werden:

alter index list_part_bix_local unusable;
--> Index wurde geändert.
alter table list_part move partition P201201;
--> Tabelle wurde geändert.

alter index list_part_bix_local rebuild;
FEHLER in Zeile 1:
ORA-14086: Ein partitionierter Index kann nicht als ganzes neu erstellt werden

Immerhin kann man alle Indizes einer Partition mit dem Kommando
alter table ... modify partition ... rebuild unusable local indexes;
neu erzeugen, was ich gelegentlich schon mal erwähnt hatte.

Fehlt noch eine Antwort auf die Frage, ob es einen guten Grund für das unterschiedliche Verhalten gibt. Dazu ist zunächst zu sagen, dass die Oracle-Dokumentation den hier dargestellten Fall explizit erläutert. Dort heißt es:
This rebuilding of the bitmap index structures is necessary to accommodate the potentially higher number of rows stored for each data block with table compression enabled. Enabling table compression must be done only for the first time. All subsequent operations, whether they affect compressed or uncompressed partitions, or change the compression attribute, behave identically for uncompressed, partially compressed, or fully compressed partitioned tables.

To avoid the recreation of any bitmap index structure, Oracle recommends creating every partitioned table with at least one compressed partition whenever you plan to partially or fully compress the partitioned table in the future. This compressed partition can stay empty or even can be dropped after the partition table creation.

Having a partitioned table with compressed partitions can lead to slightly larger bitmap index structures for the uncompressed partitions. The bitmap index structures for the compressed partitions, however, are usually smaller than the appropriate bitmap index structure before table compression. This highly depends on the achieved compression rates.
Es scheint also doch einen guten Grund für das Verhalten zu geben. Ein Blick in Julian Dykes klassische Präsentation Bitmap Internals erklärt das Verhalten: dort wird auf Folie 37 erklärt, dass für die Komprimierung der bitmaps der Hakan Factor der Tabelle relevant ist. Den Hakan Factor findet man in TAB$:

select OBJ#
     , spare1 
  from sys.tab$
 where OBJ# = 105354;

-- vor dem MOVE der Partition
      OBJ#     SPARE1
---------- ----------
    105354        736

-- nach dem MOVE der Partition
      OBJ#     SPARE1
---------- ----------
    105354     163831

Der Hakan Factor ist demnach ein Attribut der kompletten Tabelle (die OBJECT_IDs der Partitionen findet man nicht in TAB$) und ändert sich erst nach dem Neuaufbau einer Partition - also nicht bereits mit dem MODIFY PARTITION ... COMPRESS, sondern erst mit dem MOVE, also der physikalischen Reorganisation. Das Verhalten ist somit begründet - und mehr wollte ich gar nicht wissen.

Keine Kommentare:

Kommentar veröffentlichen