Tablespace-Reorganisation mit Data Pump

By | 1. März 2021

Im Leben eines DBAs kann es mal vorkommen, dass man einen Tablespace reorganisieren muss. Häufig tut man dies, um wieder Freiplatz auf dem Datenbank-Storage zu bekommen, nachdem sich ein Tablepace aufgrund eines Ereignisses „aufgebläht“ hat oder sehr viele Daten gelöscht wurden. Ich selbst hatte kürzlich das Problem, dass eine Datenbank in Folge einer Unicode-Migration von 200 auf 500 GB angewachsen ist. Bei genauerer Betrachtung stellte sich heraus, dass für das Wachstum Spalten vom Datentyp CLOB verantwortlich waren. Obwohl die CLOBs insgesamt eine Segment-Größe von 400 GB hatten, waren nur 200 GB davon tatsächlich belegt. Sprich, ich hatte über 200 GB Luft ist den CLOB-Segmenten! Da ein „Shrinking“ der dem Tablespace zugehörigen Datenbankdateien in diesem Fall nicht möglich war, musste der Tablespace also in geringer Größe neu angelegt und die Datenbankobjekte dort hineinbewegt werden. Dafür gibt es verschiedene Methoden wie z.B. das Umbewegen aller Objekte mittels „Alter … move“-Befehle. Dieses Verfahren hat allerdings den Nachteil hat, dass man die Objekte zunächst in einen „Zwischen“-Tablespace umziehen lassen muss, um sie dann noch einmal in den neu erstellten ursprünglichen Tablespace umzubewegen. Außerdem kann dies sehr schnell sehr komplex und aufwendig werden, wenn es sich dabei um sehr viele Tabelle mit vielen Constraints und Indexe handelt, welche ebenfalls berücksichtigt werden müssen.

Ich möchte in diesem Blog-Beitrag eine Methode aufzeigen, die recht robust und relativ einfach ist und Oracle Data Pump nutzt. Die Tablespace-Inhalte werden dabei exportiert und, nachdem der Tablespace wieder verkleinert angelegt wurde, wieder importiert. Bei diesem Verfahren muss man allerdings sicherstellen, dass der Tablespace, den man reorganisieren möchte, absolut in sich geschlossen ist (self-contained). Das bedeutet, dass es keine Abhängigkeiten von und zu Objekten gibt, die sich in anderen Tablespaces befinden (z.B. Constraints, Indexe usw.). Sollte dies der Fall sein, was nicht ungewöhnlich ist, muss man die anderen Tablespaces ebenfalls mit in die Reorganisation einschließen. Die Prüfung der „In-Sich-Geschlossenheit“ kann man komfortabel mit der Oracle Prozedur DBMS_TTS.TRANSPORT_SET_CHECK durchführen, welche in der Regel im Zusammenhang mit der Transaportable Tablespaces Technologie genutzt wird. Da die Daten aus der Datenbank exportiert werden, muss man gewährleisten, dass man ein Laufwerk zur Verfügung hat, in dem genug Freiplatz zur Aufnahme des Exports-Dumps vorhanden ist. Kurz erwähnen möchte ich noch, dass diese Art von Tablespace Reorganisation nicht für die Tablespaces SYSTEM und SYSAUX angewendet werden kann, da diese beiden Tablespaces ständig verfügbar sein müssen.

1. Aktuelle Größe der Tablespace anzeigen

set linesize 130 pagesize 100
col tablespace_name for a30

select tablespace_name, round(sum(bytes)/1024/1024,0) allocated_mb
from dba_data_files 
group by tablespace_name
order by tablespace_name;

Beispiel

TABLESPACE_NAME                ALLOCATED_MB
------------------------------ ------------
APPLTS                         2170
DWHTS                          210
ERPTS                          418004
SYSAUX                         26070
SYSTEM                         700
UNDOTBS1                       3170
UNICODE_REPO                   900
USERS                          5

In dem o.a. Beispiel belegt der Tablespace ERPTS belegt insgesamt ca. 408 GB und soll nun reorganisiert werden, um wieder mehr Freiplatz zu bekommen.

2. Self Containing Check durchführen

Der zu reorganisierende Tablespace muss in sich geschlossen sein (self-contained). Um dies sicherzustellen, sollte man folgende Prüfung mit dem Package DBMS_TTS.TRANSPORT_SET_CHECK durchführen.

Beispiel Check Tablespace ERPTS

SQL> execute dbms_tts.transport_set_check(full_check=> TRUE, ts_list => 'ERPTS', incl_constraints => true);

Abrage Ergebnis:

SQL> select * from transport_set_violations;
VIOLATIONS
----------------------------------------------------------------------------------------------------------------------------------
ORA-39907: Index ERP.PRODUCT_INDEX in tablespace ERPTS points to table ERP.CUSTOMER_LOGO in tablespace APPLTS.
ORA-39908: Index ERP.PRODUCT_PRIMARY$503 in tablespace ERPTS enforces primary constraints of table ERP.CUSTOMER_LOGO in tablespace APPLTS.
ORA-39907: Index ERP.PRODUCT_OBJPARENTINDEX in tablespace ERPTS points to table ERP.CUSTOMEROBJEKT in tablespace APPLTS.
ORA-39908: Index ERP.PRODUCT_OBJECT_PRIMARY$220 in tablespace ERPTS enforces primary constraints of table ERP.GRAFIKOBJEKT in tablespace APPLTS.
ORA-39907: Index ERP.PRODUCT_POLYPARENTINDEX in tablespace ERPTS points to table ERP.CUSTOMER in tablespace APPLTS.
ORA-39908: Index ERP.PRODUCT_PLG_PRIMARY$502 in tablespace ERPTS enforces primary constraints of table ERP.CUSTOMER in tablespace APPLTS.
ORA-39906: Constraint ERP_FOREIGN$3081 between table ERP.ZEITSCHEIBEN in tablespace APPLTS and table ERP.AUFTRAG in tablespace ERPTS.

In dem o.a. Beispiel zeigt die Abfrage der Violations, dass es einige Abhängigkeiten zu und von anderen Objekten in anderen Tablespaces gibt! Eine Reorganisation des Tablespace ERPTS allein würde zu Referenzierungsfehlern führen, die sich anschließend nur mit viel Aufwand beheben lassen. Dies gilt es zu vermeiden.

Wie ist hierbei nun vorzugehen? In dem Fall bleibt einem nichts anderes übrig, als den Abhängigen Tablespace (hier APPTS) ebenfalls mit in die Reorganisation einzubeziehen. Sollte es nach erneuter Prüfung weitere, kaskadierende Abhängigen zu anderen Tablespaces geben, müssten diese ebenfalls einbezogen werden.

Wiederholung des Self Containing Check inklusive dem abhängigen Tablespace APPLTS

SQL> execute dbms_tts.transport_set_check(full_check=> TRUE,ts_list => 'ERPTS,APPLTS',incl_constraints => true);
SQL> select * from transport_set_violations;
no rows selected

Das Erbebnis der Prüfung zeigt nun, dass es keine weiteren Violations gibt. Daraus folgt, dass der Tablespace ERPTS und der Tablespace APPTS zusammen reorganisiert werden müssen und es keine weiteren Objekte/Tablespaces mit Abhängigkeiten gibt

3. Anwendung und Jobs stoppen

Während der Reorganisation stehen die Anwendungsdaten in den Tablespaces nicht zur Verfügung. Um den lesenden und schreibenden Zugriff der Datenbankanwendungen zu unterbinden, sollten diese folglich gestoppt werden.

4. Archive-Logging deaktivieren

Während des geplanten Imports werden u.U. sehr viele Archivelogs erzeugt, sofern die Datenbank sich im Archivelog Modus befindet. Ein massives Schreiben von Archivelogs könnte das System über Gebühr belasten und das System zum Stillstand bringen, falls der Freiplatz der Archivelog Destination nicht ausreicht (Archiver Stuck).

Ich empfehle daher, sofern möglich, bei großen Datenimports die Datenbank in den Noarchivelog Mode zu versetzen und zur Unterbindung von Anwendungs-Sessions den Restriced Mode zu aktivieren. Der Restricted Mode verhindert, dass sich Nicht-Administationsnutzer an die Datenbank anmelden können. Des Weiteren werden Remote-Anmeldungen, die über den Listener kommen, geblockt.

sqlplus / as sysdba
SQL> shutdown immediate;
SQL> startup mount restrict;
SQL> alter database noarchivelog;
SQL> alter database open;

Falls das Setzen der Datenbank in den Noarchivelog-Mode nicht möglich ist, weil man die Datenbank nicht herunterfahren kann oder möchte, dann kann man auch beim Import den Data Pump Parameter transform=disable_archive_logging:y  setzen, welcher das Archivelogging auf ein Minimum reduziert.  Leider funktioniert diese Methode in der Praxis nicht allzu gut, da der Parameter beim Import eigentlich nur auf Objektebene das Archivelogging deaktiviert und es beim Import bei anschließender Wiederaktivierung zu Fehlern kommen kann. Außerdem hat der Parameter keine Wirkung, wenn die Datenbank im „Force Logging“ Mode läuft.

impdp .... transform=disable_archive_logging:y 

5. Datenbanksicherung prüfen bzw. anfertigen

Da eine Tablespace-Reorganisation auch einmal schief gehen kann, sollte man zwingend sicherstellen, dass man eine gültige physikalische Datenbanksicherung hat, um die Datenbank in den Zustand unmittelbar vor der Reorganisation wiederherstellen zu können. Falls keine Sicherung vorhanden ist, sollte man diese z.B. mit RMAN anfertigen.

6. Status vorhandener Invalid Objects feststellen

Um die Fehlerfreiheit nach dem Reorg feststellen zu können, ist es hilfreich den Zustand aller Datenbankobjekte vor und nach dem Reorg vergleichen zu können. Ich tue dies für gewöhnlich, indem ich die derzeitigen Objekte mit dem Status INVALID in eine zu erstellende Tabelle schreibe, um hinterher prüfen zu können, ob neue Objekte mit dem Status INVALID durch den Reorg hinzugekommen sind.

sqlplus / as sysdba
@?/rdbms/admin/utlrp

create table system.snapshot_inv_objs
as
select owner, object_name, object_type, status
from dba_objects
where status !='VALID'
order by owner;
commit;

7. Tablespaces exportieren

Die beiden Talespaces können mit Data Pump z.B. wie folgt exportiert werden.

expdp USERID=system DIRECTORY=DATA_PUMP_DIR TABLESPACES=ERPTS,APPTS DUMPFILE=ts_%u.dmp LOGFILE=expdp_ts.log FLASHBACK_TIME=SYSTIMESTAMP

8. Tablespaces droppen und neu angelegen

Nach erfolgreichem Export können die Tablespace nun gelöscht werden.

SQL> drop tablespace ERPTS including contents and datafiles cascade constraints;
SQL> drop tablespace APPTS including contents and datafiles cascade constraints;

Um sie dann anschließend mit geringerer Größe neu anzulegen.
Beispiel: Tablespace ERPTS mit einer initialen Größe von 50 GB und maximal automatisch erweiterbar auf 160 GB (5×32 GB)

SQL> create tablespace "ERPTS" datafile size 10G autoextend on next 10M maxsize unlimited;
SQL> alter tablespace "ERPT" add datafile size 10G autoextend on next 10M maxsize unlimited;
SQL> alter tablespace "ERPT" add datafile size 10G autoextend on next 10M maxsize unlimited;
SQL> alter tablespace "ERPT" add datafile size 10G autoextend on next 10M maxsize unlimited;
SQL> alter tablespace "ERPT" add datafile size 10G autoextend on next 10M maxsize unlimited;

Beispiel: Tablespace APPTS mit einer initialen Größe von 1 GB und maximal automatisch erweiterbar auf 32 GB (Unlimited)

SQL> create tablespace "APPTS" datafile size 10G autoextend on next 10M maxsize unlimited;

9. Tablespace-Inhalte wieder importieren

Die Inhalte der beiden Tablespaces können mit Data Pump z.B. wie folgt wieder importiert werden.

impdp USERID=system DIRECTORY=DATA_PUMP_DIR DUMPFILE=ts_%u.dmp LOGFILE=impdp_ts.log

Der Import sollte fehlerfrei sein.

10. Prüfung auf neue Invalid Objects

Sind neue Objekte mit Status „INVALID“ hinzugekommen? Um diese verneinen zu können, sollte die folgenden Abfrage keine Datenbankobjekte ausgeben.

sqlplus / as sysdba
set linesize 130 pagesize 600 

col owner for a25 
col object_name for a35 
col object_type for a20 
col status for a10 heading 

select owner, object_name, object_type, status
from dba_objects
where status !='VALID'
minus
select owner, object_name, object_type, status
from system.snapshot_inv_objs
order by owner, object_name;
no rows selected

Wir die Tabelle nicht mehr benötigt, kann sie gelöscht werden.

SQL> drop table system.snapshot_inv_objs;

11. Aktuelle Größe der Tablespace anzeigen

Nach erfolgter Reorganisation kann man sich abschließend das Ergebnis, sprich den Platzgewinn anzeigen lassen.

set linesize 130 pagesize 100
col tablespace_name for a30

select tablespace_name, round(sum(bytes)/1024/1024,0) allocated_mb
from dba_data_files 
group by tablespace_name
order by tablespace_name;

Beispiel

TABLESPACE_NAME                ALLOCATED_MB
------------------------------ ------------
APPLTS                         1956
DWHTS                          210
ERPTS                          205103
SYSAUX                         26070
SYSTEM                         700
UNDOTBS1                       3170
USERS                          5

Der Tablespace ERPTS belegt nun statt 418004 MB  nur noch 205103 MB!

12. Archive-Logging wieder aktivieren

Wenn im Punkt 4 das Archivelogging der Datenbank deaktviert worden, ist dann sollte dieses wieder aktiviert werden und die Datenbank im Non-Restricted Mode gestartet werden.

sqlplus / as sysdba
SQL> shutdown immediate;
SQL> startup mount;
SQL> alter database archivelog;
SQL> alter database open;

Um die Wiederherstellbarkeit der Datenbank zu gewährleisten. sollte nun noch direkt im Anschluß eine Online Sicherung durchgeführt werden.

13. Datenbanksicherung durchführen

Um die Wiederherstellbarkeit der Datenbank zu gewährleisten. sollte nun noch direkt im Anschluß eine Online Sicherung durchgeführt werden.

14. Objektstatistiken sammeln

Nach dem Reimport der Objekte der Tablespaces sind mutmaßlich einige Objekt-Statistiken nicht mehr zutreffend (Stale). Will man nicht warten bis diese im nächsten Maintenance-Fenster automatisch aktualisiert werden, kann man diese manuell aktualisieren.

Welche User haben Objekte in den importierten Tablespaces?

select distinct owner from dba_segments where tablespace_name in ('ERPTS','APPTS');
OWNER
---------------
ERP
SCOTT

Beispiel Aktualisierung der Statistiken von Tabellen und Indexe inklusive Histogramm-Statistiken der beiden Schemata ERP und SCOTT:

exec dbms_stats.gather_schema_stats('ERP', method_opt=>'for all columns size auto', estimate_percent => dbms_stats.auto_sample_size, cascade => true);
exec dbms_stats.gather_schema_stats('SCOTT', method_opt=>'for all columns size auto', estimate_percent => dbms_stats.auto_sample_size, cascade => true);

15. Anwendung und Jobs starten

Die Anwendungen und -Jobs können letztendlich nun wieder aktiviert werden.