Jak Efektywnie Kopiować Dane Między Tabelami

Poniżej przedstawiam dwa sposoby kopiowania danych między tabelami. Są to sposoby, które pokazują swoją moc, dopiero kiedy tabele są naprawdę duże. Każdy ze sposobów bazuje na innym silniku i sprawdza się w innych sytuacjach.

DROP TABLE T1
/
DROP TABLE T2
/

CREATE TABLE T1(id NUMBER, descr CHAR(2000))
/
CREATE TABLE T2(id NUMBER, descr CHAR(2000))
/

INSERT INTO T1
  SELECT ROWNUM, 'A' FROM DUAL
    CONNECT BY ROWNUM <= 1000000
/    

COMMIT
/

BEGIN
  DBMS_STATS.GATHER_TABLE_STATS (ownname => USER , tabname => 'T1', granularity => 'ALL', degree => 64);
END;
/

SELECT /*FIRST_ROWS(1)*/ bytes/1024/1024 AS mb FROM user_segments
 WHERE segment_name = 'T1'
/

Do przekopiowania jest nieco ponad 2GB danych.

MB
-------
2239.5

Pierwszy sposób bazuje na silniku PL/SQL, w którym to dane w paczkach po 50000 rekordów kopiowane są do tabeli T2. Jak każde rozwiązanie, również i to posiada swoje wady i zalety:

Zalety:

  • Możliwość przechowywania mechanizmu w ciele procedur i funkcji
  • Możliwość zwalniania pamięci podręcznej co określoną liczbę określoną parametrem LIMIT, co mniej obciąża dziennik wycofania.
  • Podgląd postępu kopiowania w drugiej sesji dzięki temu, że polecenie COMMIT wykonywane jest co 50k rekordów

Wady:

  • Wymuszony context switching, czyli przepinanie się między silnikami SQL i PL/SQL
  • Zaangażowanie dwóch silników: SQL i PL/SQL
  • Brak bezpośredniego działania na silniku SQL
  • Zaangażowanie buffer cache, czyli części w pamięci SGA, która odpowiedzialna jest za przechowywanie bloków danych
DECLARE
  cur SYS_REFCURSOR;
  TYPE t IS TABLE OF T1%ROWTYPE;
  l_t1 t;  
BEGIN
  OPEN cur FOR ('SELECT * FROM T1');
  LOOP
    FETCH cur BULK COLLECT INTO l_t1 LIMIT 50000;
    EXIT WHEN l_t1.COUNT <= 0;
    
    FORALL x IN 1..l_t1.COUNT
      INSERT INTO T2 VALUES l_t1(x);

    COMMIT;
  END LOOP;
  CLOSE cur;

  COMMIT;
END;
/
Task completed in 13.815 seconds

Rozwiązanie numer dwa bazuje bezpośrednio na silniku SQL i wykorzystuje ‘wspomagacze’, w postaci ALTER’ów na sesji, które wymuszają zrównoleglenie na instrukcjach i braku zapisu do REDO.

Zalety:

  • Najbardziej oczekiwany przy hurtowniach danych
  • Możliwość pominięcia buffer cache
  • Bazuje na czystym silniku SQL
  • Brak context switchingu
  • Możliwośc zrównoleglenia operacji
  • Wstawianie rekordów bezposrednio za High Water Mark

Wady:

  • Kopiowanie odbywa się w całości
  • Brak możliwośc podglądu ile i jakie rekordy zostały już skopiowane
  • W przypadku wystąpienia błędu wycofywana jest całość danych

ALTER SESSION SET DB_FILE_MULTIBLOCK_READ_COUNT=512
/
ALTER SESSION SET DDL_LOCK_TIMEOUT = 1200
/
ALTER SESSION FORCE PARALLEL DML PARALLEL 8
/
ALTER SESSION FORCE PARALLEL QUERY PARALLEL 8
/
ALTER TABLE T2 NOLOGGING
/

INSERT /*+ APPEND PARALLEL(8) */ INTO T2
  SELECT /*+ PARALLEL(8) */ * FROM T1
/

COMMIT
/

ALTER TABLE T2 LOGGING
/
Task completed in 7.277 seconds

Choć drugi sposób kopiowania danych wydaje się dużo szybszy, to nie jest to remedium na każde powolne kopiowanie danych. Do każdej czynności kopiowania danych należy podchodzić indywidualnie i brać pod uwagę takie elementy jak utworzone indexy, partycje na tabeli, joby działające w tle, charakterystyke środowiska (OLAP czy OLTP), ilość dostępnej przestrzeni dyskowej, czy choćby wielkość pojedynczego rekordu.

Szymon

Komentarze |0|

Legenda *) Pola oznaczone gwiazdką są wymagane
**) Możesz używać tych znaczników i atrybutów HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>