it-swarm.dev

Uproszczone / zautomatyzowane odzyskiwanie wolnego miejsca na dysku danych

W wersji Oracle 11g:

Po Googlingu nie mogę znaleźć prostego sposobu na odzyskanie wolnego miejsca po usunięciu tabeli.

Znalazłem wiele wyjaśnień, mówiących o tym, jak plik danych ulega fragmentacji, duży stos nudnych zapytań, które musisz uruchomić, aby przenieść „puste miejsce” na końcu pliku danych (tabela po tabeli ... nawet gdy masz 200 stolików!?).

Następnie musisz zmniejszyć rozmiar pliku danych, „zgadując”, o ile możesz go zmniejszyć, albo musisz dokładnie wiedzieć, jaki jest twój „rozmiar bloku” ... I wreszcie nie powinieneś zapominać o „odbudowaniu indeksów”.

Patrz na przykład: http://asktom.Oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:54178027703899

i http://www.Oracle-base.com/articles/misc/ReclaimingUnusedSpace.php

Czy istnieje prosta procedura PL/SQL, która, biorąc pod uwagę nazwę obszaru tabel lub nazwę pliku danych, wykonałaby to zadanie? Lub jakieś podobne narzędzie Oracle?

8
Frosty Z

Krótka odpowiedź brzmi nie. Niestety sposób na zrobienie tego w Oracle wymaga „dużego stosu nudnych zapytań”. Artykuły, do których prowadzą linki, to jedne z najlepszych dostępnych informacji na ten temat. Plik danych rzeczywiście ulega fragmentacji, więc nawet jeśli wolna przestrzeń istnieje poniżej najwyższego segmentu, Oracle nie skonsoliduje go automatycznie po wykonaniu RESIZE.

Aby „zdefragmentować” przestrzeń tabel, należy przenieść te segmenty na początek pliku danych, a nie na koniec. W przypadku tabel jest to proces offline, co oznacza, że ​​tabela będzie niedostępna podczas przenoszenia. Indeksy można przenosić w trybie offline lub w wersji Enterprise Edition i w trybie online. Ponieważ masz okno awarii, zalecamy wykonanie tych kroków.

A. Zmniejsz pliki danych z wolną przestrzenią poza znakiem wysokiej wody. Można to zrobić w następujący sposób (zapytanie jest podobne do procedury 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. Po zmniejszeniu rzeczy powyżej znaku wysokiej wody, dowiedz się, jakie przestrzenie tabel nadal skorzystałyby z przesunięcia segmentów.

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. Dla każdego z tych obszarów tabel określ, które segmenty należy przenieść. (Zamień USERS na nazwę swojego obszaru tabel lub dołącz go do poprzedniego zapytania)

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. Przenieś każdą tabelę i odbuduj indeksy i statystyki.

E. Powtórz krok A.

Właśnie zbudowałem większość tych zapytań, więc będziesz chciał je dokładnie przetestować przed użyciem. Przypuszczam, że możesz stworzyć procedurę, która użyłaby EXECUTE IMMEDIATE, aby utworzyć rzeczywiste instrukcje, które będą działały dynamicznie, ale ponieważ zapytania otrzymają ORA-08103: Obiekt nie istnieje już podczas przenoszenia, myślę, że najlepiej jest kontrolować ten proces ręcznie, nawet jeśli oznacza to nieco więcej czasu /wysiłek.

5
Leigh Riffel

Częściowe rozwiązanie inspirowane ta strona :

Nie reorganizuje wolnego miejsca, ale automatycznie wykrywa dostępne wolne miejsce na końcu plików danych i drukuje odpowiednie polecenia „RESIZE”.

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;
3
Frosty Z

Po kilku dniach surfowania w Google znalazłem najprostszy i najprostszy przykład odzyskania wolnego miejsca w obszarze tabel po usunięciu. mam nadzieję, że to pomoże

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

rozwiązanie:

ALTER TABLE MOVE demo

Utwórzmy tabelę z 9999 rzędami, każdy o wielkości około 1k:

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.

Tabela ma przydzielone 29 zakresów, w sumie 14,6 mln:

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

Usuńmy wszystkie wiersze:

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

Teraz - „niespodzianka” - tabela nadal używa tych samych rozmiarów:

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

Dlaczego ? Ponieważ nawet jeśli usuniesz wszystkie wiersze tabeli, Znak Wysokiej Wody nie jest obniżany - nigdy nie jest obniżany, aby pozwolić na maksymalną współbieżność (Oracle poważnie podchodzi do maksymalizacji współbieżności, tj. Wydajności i skalowalności; jest to główna przyczyna jego sukcesu w aplikacjach Enterprise).

Zwolnienie nieużywanego miejsca (= spacja nad HWM) niewiele pomaga (ponieważ nie ma dużo nieużywanego miejsca nad HWM):

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

Teraz PRZESUŃ tabelę, co w istocie oznacza klonowanie tabeli (w tym wyzwalaczy, ograniczeń itd.), Przeniesienie wierszy, upuszczenie „starej” tabeli i zmianę nazwy nowej - wszystko wykonane przez jądro, tak super-bezpieczne nawet w przypadku awarii komputera/serwera:

SQL> alter table t move;
Table altered.

Teraz mamy przydzielony tylko początkowy zakres:

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

Uwaga: zwykle zdarza się, że wiele/wszystkie indeksy w tabeli są NIEUŻYWANE po przeniesieniu (nie w tym przypadku, ale korzystam z najnowszej wersji 9.2.0.4, która prawdopodobnie zoptymalizowała proces w przypadku całkowicie pustych tabel ):

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

Jeśli STATUS nie był WAŻNY, możesz po prostu ręcznie odbudować indeksy:

SQL> alter index SYS_C002573 rebuild;
Index altered.

Lub możesz zautomatyzować cały proces:

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;
/

Na przykład ustawmy ręcznie indeks na UNUSABLE:

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

2
vishal0108

Zanim w ogóle spróbujesz zmniejszyć pliki danych, zadaj sobie pytanie: czy zamierzasz kiedyś tworzyć nowe segmenty w powiązanym obszarze tabel w niedalekiej przyszłości? Jeśli tak, nie ma sensu się kurczyć. Przestrzeń zostanie ponownie wykorzystana na nowe segmenty, a Ty zaoszczędzisz sobie i systemowi wiele wysiłku, pozostawiając ją taką, jaka jest.

2
Uwe Hesse

Jak wspomniano wcześniej, będziesz musiał przenieść wszystkie ponad 200 tabel w tym obszarze tabel, aby zwolnić miejsce w pliku danych, a następnie zmienić rozmiar, aby odzyskać miejsce. Ale zamiast uruchamiać wszystkie te zapytania, 12c Enterprise manager wykonuje to zadanie. Będziesz musiał przejść do opcji Strona główna bazy danych> Pamięć> Przestrzeń tabel. Wybierz obszar tabel, nad którym chcesz pracować, i kliknij Reorganizuj. Daje opcję przeglądania instrukcji SQL, które mają zostać wykonane. Możesz wziąć ich kopię i uruchomić ją samodzielnie lub zaplanować pracę w EM.

W rzeczywistości tworzy inny obszar tabel, przenosi wszystkie obiekty do nowego obszaru tabel, odbudowuje indeksy i upuszcza obiekty ze starego obszaru tabel.

Jest kilka wad, o których mogę myśleć. Powinno to zostać wykonane poza godzinami szczytu, w przeciwnym razie wystąpi błąd informujący, że zasób jest zajęty. W pliku danych (nie w obszarze tabel) na końcu będzie dodawane „reorg”.

1
Srikanthan