Vereinfachte / automatisierte Rückgewinnung von freiem Speicherplatz für Datendateien


8

Unter Oracle Version 11g:

Nach dem Googeln kann ich keinen einfachen Weg finden, um nach dem Löschen einer Tabelle freien Speicherplatz zurückzugewinnen.

Ich habe viele Erklärungen gefunden, die erklären, wie die Datendatei fragmentiert wird, den großen Stapel langweiliger Abfragen, die Sie ausführen müssen, um den "leeren Raum" am Ende der Datendatei zu verschieben (Tabelle für Tabelle ... auch wenn Sie habe 200 Tische !?).

Dann müssen Sie die Größe der Datendatei reduzieren, indem Sie "raten", um wie viel Sie sie reduzieren können, oder Sie müssen genau wissen, wie groß Ihre "Blockgröße" ist ... Und schließlich sollten Sie nicht vergessen, "die Indizes neu zu erstellen".

Siehe zum Beispiel: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:54178027703899

und http://www.oracle-base.com/articles/misc/ReclaimingUnusedSpace.php

Gibt es eine einfache PL / SQL-Prozedur, die bei gegebenem Tabellenbereichs- oder Datendateinamen für diesen Job geeignet ist? Oder ein ähnliches Oracle-Tool?


Interessante Informationen: Überprüfen Sie, ob Ihre Tablespaces "lokal verwaltet" oder "verzeichnisverwaltet" sind. Ersteres scheint eine bessere Handhabung von "Defragmentierungs" -Datendateien zu haben. Siehe: orafaq.com/node/3
Frosty Z

Antworten:


5

Die kurze Antwort lautet Nein . Leider erfordert der Weg, dies in Oracle zu tun, den "großen Stapel langweiliger Abfragen". Die Artikel, auf die Sie verlinkt haben, gehören zu den besten verfügbaren Informationen zu diesem Thema. Die Datendatei wird tatsächlich fragmentiert, sodass Oracle sie nicht automatisch konsolidiert, selbst wenn unter dem höchsten Segment freier Speicherplatz vorhanden ist, wenn a RESIZEfertig ist.

Um den Tabellenbereich zu "defragmentieren", müssen Sie diese Segmente an den Anfang der Datendatei und nicht an das Ende verschieben. Bei Tabellen ist dies ein Offline-Prozess, dh die Tabelle ist während des Verschiebens nicht verfügbar. Indizes können entweder offline oder mit Enterprise Edition online verschoben werden. Da Sie ein Ausfallfenster haben, empfehle ich Ihnen, diese Schritte auszuführen.

A. Verkleinern Sie Datendateien mit freiem Speicherplatz jenseits der Hochwassermarke. Dies kann wie folgt erfolgen (die Abfrage ähnelt der Prozedur von Frosty Z):

SELECT ceil( blocks*(a.BlockSize)/1024/1024) "Current Size",
   ceil( (nvl(hwm,1)*(a.BlockSize))/1024/1024 ) "Smallest Poss.",
   ceil( blocks*(a.BlockSize)/1024/1024) -
   ceil( (nvl(hwm,1)*(a.BlockSize))/1024/1024 ) "Savings",
   'alter database datafile '''|| file_name || ''' resize ' || 
      ceil((nvl(hwm,1)*(a.BlockSize))/1024/1024/100)*100  || 'm;' "Command"
FROM (SELECT a.*, p.value BlockSize FROM dba_data_files a 
JOIN v$parameter p ON p.Name='db_block_size') a
LEFT JOIN (SELECT file_id, max(block_id+blocks-1) hwm FROM dba_extents GROUP BY file_id ) b
ON a.file_id = b.file_id
WHERE ceil( blocks*(a.BlockSize)/1024/1024) - ceil( (nvl(hwm,1)*(a.BlockSize))/1024/1024 ) 
   > 100 /* Minimum MB it must shrink by to be considered. */
ORDER BY "Savings" Desc;

B. Nachdem Sie Dinge über die Hochwassermarke geschrumpft haben, finden Sie heraus, welche Tablespaces noch davon profitieren würden, wenn Segmente verschoben würden.

SELECT DISTINCT tablespace_name FROM
(      
    SELECT tablespace_name, block_id + blocks LastBlock,
       lead(block_id) OVER (PARTITION BY File_ID 
          ORDER BY tablespace_name, file_id, block_id) NextBlock
       FROM dba_free_space 
) WHERE LastBlock <> NextBlock AND NextBlock IS NOT NULL;

C. Bestimmen Sie für jeden dieser Tabellenbereiche, welche Segmente verschoben werden müssen. (Ersetzen Sie USERS durch den Namen Ihres Tablespace oder verbinden Sie ihn mit der vorherigen Abfrage.)

SELECT distinct de.segment_name
FROM dba_extents de
JOIN
(
   SELECT tablespace_name, file_id, MIN(block_id) LowestFreeBlock
   FROM dba_free_space
   WHERE tablespace_name = 'USERS'
  GROUP BY tablespace_name, file_id
) dfs ON dfs.tablespace_name = de.tablespace_name AND dfs.file_id = de.file_id
WHERE de.tablespace_name = 'USERS'
AND de.block_id > dfs.LowestFreeBlock;

D. Verschieben Sie jede Tabelle und erstellen Sie die Indizes und Statistiken neu.

E. Wiederholen Sie Schritt A.

Ich habe gerade die meisten dieser Abfragen erstellt, daher sollten Sie sie vor der Verwendung gründlich testen. Ich nehme an, Sie könnten eine Prozedur EXECUTE IMMEDIATEerstellen, mit der die tatsächlichen Anweisungen für die dynamische Ausführung erstellt werden. Da Abfragen jedoch ORA-08103 erhalten: Das Objekt ist während des Verschiebens nicht mehr vorhanden. Ich denke, es ist am besten, diesen Prozess auch manuell zu steuern wenn es etwas mehr Zeit / Mühe bedeutet.


3

Teillösung inspiriert von dieser Seite :

Es organisiert den freien Speicherplatz nicht neu, erkennt jedoch automatisch den verfügbaren freien Speicherplatz am Ende der Datendateien und druckt die richtigen 'RESIZE'-Befehle aus.

DECLARE
    BLKSIZE INTEGER;

BEGIN
    SELECT VALUE INTO BLKSIZE FROM V$PARAMETER WHERE NAME = 'db_block_size';

    FOR INDEX_ROW IN (
      SELECT 'ALTER DATABASE DATAFILE ''' || FILE_NAME || ''' RESIZE ' || CEIL( (NVL(HWM,1)*BLKSIZE)/1024/1024 ) || 'M;' SHRINK_DATAFILES FROM DBA_DATA_FILES DBADF,
            (SELECT FILE_ID, MAX(BLOCK_ID+BLOCKS-1) HWM FROM DBA_EXTENTS GROUP BY FILE_ID ) DBAFS
            WHERE DBADF.FILE_ID = DBAFS.FILE_ID(+) AND CEIL(BLOCKS*BLKSIZE/1024/1024)- CEIL((NVL(HWM,1)* BLKSIZE)/1024/1024 ) > 0
    ) LOOP
        DBMS_OUTPUT.PUT_LINE(INDEX_ROW.SHRINK_DATAFILES);
    END LOOP;
END;

2

Bevor Sie versuchen, Datendateien überhaupt zu verkleinern, fragen Sie sich: Werden Sie in nicht allzu ferner Zukunft wieder neue Segmente im zugehörigen Tabellenbereich erstellen? Wenn ja, macht das Schrumpfen keinen Sinn. Der Speicherplatz wird nur für Ihre neuen Segmente wiederverwendet, und Sie sparen sich und dem System viel Aufwand, indem Sie ihn unverändert lassen.


2

Nachdem ich tagelang bei Google gesurft habe, habe ich das einfachste und klarste Beispiel gefunden, um den freien Speicherplatz im Tablespace nach dem Löschen zurückzugewinnen. ich hoffe das hilft

Link: http://www.dbforums.com/oracle/976248-how-reduce-tablespaces-used-space-after-delete-records-2.html

Lösung:

ALTER TABLE MOVE demo

Erstellen wir eine Tabelle mit 9999 Zeilen mit einer Größe von jeweils ca. 1 KB:

SQL> create table t (x char(1000) default 'x' primary key);
Table created.
SQL> insert /*+ append nologging */ into t(x) select rownum from all_objects where rownum < 10000;
9999 rows created.
SQL> commit;
Commit complete.

Der Tabelle sind 29 Bereiche zugeordnet, insgesamt 14,6 Millionen:

SQL> select count(*), sum(bytes) from user_extents where segment_name='T';
COUNT(*) SUM(BYTES)
---------- ----------
29 14680064

Löschen wir ALLE Zeilen:

SQL> delete from t;
9999 rows deleted.
SQL> commit;
Commit complete.

Nun - "Überraschung" - die Tabelle verwendet immer noch die gleichen Ausmaße:

SQL> select count(*), sum(bytes) from user_extents where segment_name='T';
COUNT(*) SUM(BYTES)
---------- ----------
29 14680064

Warum ? Denn selbst wenn Sie alle Zeilen der Tabelle löschen, wird die High Water Mark nicht verringert - sie wird nie verringert, um maximale Parallelität zu ermöglichen (Oracle ist absolut ernsthaft mit der Maximierung der Parallelität, dh Leistung und Skalierbarkeit; dies ist der Hauptgrund für den Erfolg in Unternehmensanwendungen).

Die Freigabe des nicht genutzten Speicherplatzes (= Speicherplatz über dem HWM) hilft nicht viel (da über dem HWM nicht viel nicht genutzter Speicherplatz vorhanden ist):

SQL> alter table t deallocate unused;
Table altered.
SQL> select count(*), sum(bytes) from user_extents where segment_name='T';
COUNT(*) SUM(BYTES)
---------- ----------
29 13959168

Verschieben wir nun die Tabelle, was im Wesentlichen bedeutet, die Tabelle zu klonen (einschließlich Trigger, Einschränkungen usw.), die Zeilen zu übertragen, die "alte" Tabelle zu löschen und die neue umzubenennen - alles vom Kernel erstellt, also super sicher auch bei Maschinen- / Serverausfall:

SQL> alter table t move;
Table altered.

Jetzt haben wir nur noch den anfänglichen Umfang zugewiesen:

SQL> select count(*), sum(bytes) from user_extents where segment_name='T';
COUNT(*) SUM(BYTES)
---------- ----------
1 65536

Vorsichtsmaßnahme: Normalerweise sind viele / alle Indizes in der Tabelle nach dem Verschieben NICHT VERWENDBAR (nicht in diesem Fall, aber ich verwende 9.2.0.4, die neueste Version, die den Prozess bei vollständig leeren Tabellen wahrscheinlich optimiert hat ):

SQL> col table_name form a30
SQL> col index_name form a30
SQL> set lines 123 
SQL> select table_name, index_name, status from user_indexes where table_name='T';

TABLE_NAME INDEX_NAME STATUS
------------------------------ ------------------------------ ------------------------
T SYS_C002573 VALID

Wenn STATUS nicht GÜLTIG wäre, könnten Sie die Indizes einfach manuell neu erstellen:

SQL> alter index SYS_C002573 rebuild;
Index altered.

Oder Sie können den gesamten Prozess automatisieren:

set serveroutput on size 100000
begin
for n in (select index_name from user_indexes where status <> 'VALID') loop
dbms_output.put_line ('rebuilding ' || n.index_name);
execute immediate 'alter index ' || n.index_name || ' rebuild';
end loop;
end;
/

Lassen Sie uns als Beispiel den Index manuell auf UNUSABLE setzen:

SQL> alter index SYS_C002573 unusable;
Index altered.

SQL> set serveroutput on size 100000
SQL> begin
2 for n in (select index_name from user_indexes where status <> 'VALID') loop
3 dbms_output.put_line ('rebuilding ' || n.index_name);
4 execute immediate 'alter index ' || n.index_name || ' rebuild';
5 end loop;
6 end;
7 /
rebuilding SYS_C002573

PL/SQL procedure successfully completed.

HTH Alberto


1

Wie bereits erwähnt, müssen Sie alle mehr als 200 Tabellen in diesem Tabellenbereich verschieben, um Speicherplatz in Ihrer Datendatei freizugeben, und dann die Größe ändern, um den Speicherplatz zurückzugewinnen. Anstatt all diese Abfragen auszuführen, führt 12c Enterprise Manager diese Aufgabe aus. Sie müssen zu Database Home> Storage> Tablespace navigieren. Wählen Sie den Tabellenbereich aus, an dem Sie arbeiten möchten, und klicken Sie auf Neu organisieren. Es gibt eine Option zum Anzeigen der SQL-Anweisungen, die ausgeführt werden sollen. Sie können entweder eine Kopie davon nehmen und selbst ausführen oder einen Job in EM planen.

Es erstellt tatsächlich einen weiteren Tabellenbereich, verschiebt alle Objekte in den neuen Tabellenbereich, erstellt die Indizes neu und löscht die Objekte aus dem alten Tabellenbereich.

Ich kann mir einige Nachteile vorstellen. Dies sollte außerhalb der Spitzenzeiten erfolgen, da sonst ein Fehler auftritt, der besagt, dass die Ressource ausgelastet ist. Der Datendatei (nicht dem Tablespace) wird gegen Ende "reorg" hinzugefügt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.