Domanda

I gestire un programma che ha un grande (quasi 1 TB di dati con più di 500 milioni di righe in una tabella) end Oracle database back. Il database in realtà non fa nulla (nessun sprocs, nessun trigger o nulla) è solo un archivio di dati.

Ogni mese sono tenuti a record di spurgo dal due delle tabelle principali. I criteri di spurgo varia ed è una combinazione di età riga e un paio di campi di stato. Noi di solito finisce per spurgo tra i 10 ei 50 milioni di righe al mese (aggiungiamo circa 3-5 milioni di righe di una settimana attraverso le importazioni).

Al momento dobbiamo fare questa eliminazione in lotti di circa 50.000 file (es. Cancellazione 50000, Comit, di eliminazione 50000, commit, ripetizione). Il tentativo di eliminare l'intero lotto tutto in una volta fa la banca dati non risponde per circa un'ora (a seconda del # di righe). L'eliminazione dei file in batch come questo è molto agitato nel sistema e noi di solito hanno a che fare "come il tempo lo permette" nel corso di una settimana; permettendo lo script da eseguire in modo continuo può provocare l'un degrado delle prestazioni che è inaccettabile per l'utente.

Credo che questo tipo di lotto eliminazione degrada anche performance dell'indice e ha altri impatti che alla fine causa la performance del database a degradare. Ci sono 34 indici su un solo tavolo, e la dimensione dei dati di indice è in realtà più grandi dei dati stessi.

Ecco lo script che uno dei nostri usi per fare questo spurgo:

BEGIN
LOOP

delete FROM tbl_raw 
  where dist_event_date < to_date('[date]','mm/dd/yyyy') and rownum < 50000;

  exit when SQL%rowcount < 49999;

  commit;

END LOOP;

commit;

END;

Questo database deve essere fino 99,99999% e abbiamo solo una finestra di manutenzione due giorni una volta all'anno.

Sto cercando un metodo migliore per la rimozione di questi record, ma non ho ancora trovato alcuna. Qualche suggerimento?

È stato utile?

Soluzione

La logica con la 'A' e 'B' potrebbe essere "nascosto" dietro un virtual colonna su cui si potrebbe fare il partizionamento:

alter session set nls_date_format = 'yyyy-mm-dd';
drop   table tq84_partitioned_table;

create table tq84_partitioned_table (
  status varchar2(1)          not null check (status in ('A', 'B')),
  date_a          date        not null,
  date_b          date        not null,
  date_too_old    date as
                       (  case status
                                 when 'A' then add_months(date_a, -7*12)
                                 when 'B' then            date_b
                                 end
                        ) virtual,
  data            varchar2(100) 
)
partition   by range  (date_too_old) 
( 
  partition p_before_2000_10 values less than (date '2000-10-01'),
  partition p_before_2000_11 values less than (date '2000-11-01'),
  partition p_before_2000_12 values less than (date '2000-12-01'),
  --
  partition p_before_2001_01 values less than (date '2001-01-01'),
  partition p_before_2001_02 values less than (date '2001-02-01'),
  partition p_before_2001_03 values less than (date '2001-03-01'),
  partition p_before_2001_04 values less than (date '2001-04-01'),
  partition p_before_2001_05 values less than (date '2001-05-01'),
  partition p_before_2001_06 values less than (date '2001-06-01'),
  -- and so on and so forth..
  partition p_ values less than (maxvalue)
);

insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('B', date '2008-04-14', date '2000-05-17', 
 'B and 2000-05-17 is older than 10 yrs, must be deleted');


insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('B', date '1999-09-19', date '2004-02-12', 
 'B and 2004-02-12 is younger than 10 yrs, must be kept');


insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('A', date '2000-06-16', date '2010-01-01', 
 'A and 2000-06-16 is older than 3 yrs, must be deleted');


insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('A', date '2009-06-09', date '1999-08-28', 
 'A and 2009-06-09 is younger than 3 yrs, must be kept');

select * from tq84_partitioned_table order by date_too_old;

-- drop partitions older than 10 or 3 years, respectively:

alter table tq84_partitioned_table drop partition p_before_2000_10;
alter table tq84_partitioned_table drop partition p_before_2000_11;
alter table tq84_partitioned_table drop partition p2000_12;

select * from tq84_partitioned_table order by date_too_old;

Altri suggerimenti

La soluzione classica a questo è di le tabelle, per esempio per mese o per settimana. Se non si è incontrato prima, una tabella partizionata è come diverse tabelle identicamente strutturati con un UNION implicita nella scelta, e Oracle memorizza automaticamente una riga nella partizione appropriata durante l'inserimento in base ai criteri di partizionamento. Lei parla di indici - oltre ogni partizione ottiene i propri indici partizionati troppo. Si tratta di un'operazione molto a buon mercato a Oracle di ritirare una partizione (è analogo ad un TRUNCATE in termini di carico perché questo è ciò che si sta realmente facendo - troncare o far cadere uno di questi sotto-tabelle invisibili). Sarà una quantità significativa di elaborazione di partizione "dopo il fatto", ma non ha senso piangere latte sopra rovesciato - i vantaggi a farlo superano di gran lunga i costi. Ogni mese si dovrebbe dividere la partizione in alto per creare una nuova partizione per i dati del mese successivo (si può THS automatizzare facilmente con DBMS_JOB).

E con le partizioni è possibile anche sfruttare le parallela di query e eliminazione partizione , che dovrebbe rendere gli utenti molto felice ...

Un aspetto da considerare è quanto del risultato di eliminazione prestazioni da indici e quanto dalla tavola grezza. Ogni record eliminato dalla tabella richiede la stessa delezione della fila da ogni indice btree. Se hai 30 + btree indici, ho il sospetto che la maggior parte del tempo viene speso nella manutenzione degli indici.

Questo ha un impatto sulla utilità di partizionamento. Diciamo che hanno un indice su nome. Un indice di serie Btree, tutto in un unico segmento, potrebbe avere a che fare quattro salti per ottenere dal blocco principale al blocco foglia e un quinto di lettura per ottenere la riga. Se tale indice è divisa in 50 segmenti e non avere la chiave partizione come parte della query, allora ciascuno di questi 50 segmenti dovranno essere controllati. Ogni segmento sarà più piccolo, così si può avere solo per fare 2 salti, ma si può ancora finire per fare 100 legge piuttosto che il precedente 5.

Se sono indici bitmap, le equazioni sono diversi. Probabilmente non si utilizza gli indici per identificare singole righe, ma piuttosto gruppi di essi. Quindi, piuttosto che una query utilizzando 5 IO per restituire un singolo record, è stato con 10.000 IO. Come tale il sovraccarico in partizioni extra per l'indice non sarà questione.

la cancellazione di 50 milioni di dischi al mese in lotti di 50.000 è solo 1000 iterazioni. se 1 elimina ogni 30 minuti che dovrebbe soddisfare le vostre esigenze. un'operazione pianificata per eseguire la query che hai postato, ma rimuovere il ciclo in modo viene eseguito solo una volta non dovrebbe causare un degrado evidente per gli utenti. Facciamo circa lo stesso volume di record nel nostro stabilimento di produzione che corre praticamente 24/7 e soddisfa le nostre esigenze. Noi in realtà diffonderlo fuori un po 'più di 10.000 record ogni 10 minuti, che esegue in circa 1 o 2 secondi in esecuzione sui nostri server Oracle Unix.

Se lo spazio su disco non è ad un premio, si potrebbe essere in grado di creare un ' "opera" copia della tabella, dicono my_table_new, utilizzando CTAS (Crea tabella come Select) con criteri che omette il record per essere caduto. Si può fare l'istruzione CREATE in parallelo, e con il suggerimento di accodamento per renderlo veloce, e poi costruire tutti gli indici. Poi, una volta che è finito, (e testato), rinominare la tabella esistente per my_table_old e rinominare la tabella "lavoro" per my_table. Una volta che hai dimestichezza con tutto drop my_table_old purge per sbarazzarsi del vecchio tavolo. Se ci sono un sacco di vincoli di chiave esterna, date un'occhiata al dbms_redefinition PL / pacchetto SQL . Sarà clonare gli indici, vincoli, ecc quando si utilizzano le opzioni appropriate. Questo è un riepilogo di un suggerimento di Tom Kyte di AskTom fama. Dopo la prima esecuzione, è possibile automatizzare tutto, e la creazione di tabella dovrebbe andare molto più veloce, e può essere fatto mentre il sistema è attivo, e il downtime delle applicazioni sarebbe limitato a meno di un minuto per fare la ridenominazione dei tavoli. Utilizzando CTAS sarà molto più veloce di fare diverse eliminazioni batch. Questo approccio può essere particolarmente utile se non si dispone di partizionamento licenza.

Esempio CTAS, mantenendo le righe con i dati degli ultimi 365 giorni e flag_inactive = 'N':

create /*+ append */ table my_table_new 
   tablespace data as
   select /*+ parallel */ * from my_table 
       where some_date >= sysdate -365 
       and flag_inactive = 'N';

-- test out my_table_new. then if all is well:

alter table my_table rename to my_table_old;
alter table my_table_new rename to my_table;
-- test some more
drop table my_table_old purge;

quando far cadere una partizione, si lascia indici globali inutilizzabile, che hanno bisogno di ricostruire, la ricostruzione degli indici globali sarebbe un grosso problema, come se lo si fa on-line, sarà piuttosto lento, altrimenti è necessario i tempi di inattività. in entrambi i casi, non può andare bene per il requisito.

"Noi di solito finiscono per spurgo tra i 10 ei 50 milioni di righe al mese"

Lo consiglio usando PL / SQL in lotti cancellare, diverse ore è ok credo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a dba.stackexchange
scroll top