A partire dalla versione 10g, Oracle ha implementato il recyclebin, che non è altro che il cestino.
Chi non ha mai sbrasato una tabella fondamentale del suo schema, piena zeppa di dati più che fondamentali, perdendo diversi anni di vita nello sgomento, pensando a come insabbiare il tutto o scaricare immediatamente la responsabilità su qualche meccanismo di backup/ripristino andato a male? evidentemente alla Oracle qualcuno ha fatto presente questo scenario, e quindi si sono inventati il recyclebin, e quindi tanto di cappello, considerando che parliamo di database.
Ad ogni modo, chissà come, chissà quando, chissà quale algoritmo lunare avranno inventato, ma guarda che strano cancello roba e non mi si libera lo storage, ma che problema ci sarà, chi sia stato non si sa.... ed ecco svelato ilcano. Il recyclebin rinomina gli oggetti cancellati, loggandosi ovviamente la cancellazione e togliendoli dalle consuete viste di sistema che listano oggetti.
Piccola digressione: non ho ancora capito se una tabella in oracle si può rinominare o no. diversi tool di gestione database consentono la rinomina, ma di fatto dietro le quinte creano una nuova tabella, ci travasano i dati e cancellano la vecchia, il che potrebbe anche fare incazzare più di qualcuno, tra indici, statistiche, ottimizzazioni e cavoli a merenda.
Ad ogni modo, strumento utilissimo che vi salva appunto le terga nel momento in cui cancellate per sbaglio una oggetto. Attenzione, parliamo di oggetti, non di dati, per cui tutto questo discorso vale solo se cancellate ad esempio un intera tabella, non certo se fate un bel DELETE FROM, da quello non vi salva certo il recyclebin ma dovete affidarvi ai meccanismi di rollback, flashback, flash gordon e capitan america che ritenete più opportuni, ammesso che li abbiate mai attivati sul vostro db, visto che su Oracle qualsiasi virgola muovi serve un team di architetti di sistema con almeno 3 anni di servizio sulla USS Enterprise.
Per cui, caro amico, se hai fatto DELETE o TRUNCATE sui dati e speravi di aver trovato ciò che ti avrebbe salvato, una pacca sulla spalla non te la leva nessuno, puoi riprendere a googlare e a sudare freddo.
Vediamo Come funziona
Ci sono due viste di RB(chiameremo RB il recyclebin): USER_RECYCLEBIN e DBA_RECYCLEBIN.
Di defaut, se leggete RECYCLEBIN, state leggendo USER_RECYCLEBIN. Sempre di default, il RB
è abilitato di default dalla 10g, ma si può disabilitare dal parametro RECYCLEBIN a livello di sistema o di sessione.
Come dicevamo, quando droppiamo un oggetto, questo viene rinominato, e ovviamente tutti gli altri oggetti eliminati a cascata(indici, chiavi, trigger, ecc), con un nome che comincia con BIN$.
Vediamo un esempio, creiamo una tabella con un record di prova
SQL> create table tst (col varchar2(10), row_chng_dt date); Table created. SQL> insert into tst values ('Version1', sysdate); 1 row created. SQL> select * from tst ; COL ROW_CHNG ---------- -------- Version1 16:10:03
Se il RB è attivo, droppando la tabella, la ritroveremo nella vista RECYCLEBIN:
SQL> drop table tst;
Table dropped.
SQL> select object_name, original_name, type, can_undrop as "UND", can_purge as "PUR", droptime
2 from recyclebin
SQL> /
OBJECT_NAME ORIGINAL_NAME TYPE UND PUR DROPTIME
------------------------------ ------------- ----- --- -------------------
BIN$HGnc55/7rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:10:12
Da notare che i dati sono ancora in tabella e possono essere recuperati con una normale query, ovviamente usando il nuovo nome assegnato alla tabella:
SQL> alter session set nls_date_format='HH24:MI:SS' ; Session altered. SQL> select * from "BIN$HGnc55/7rRPgQPeM/qQoRw==$0" ; COL ROW_CHNG ---------- -------- Version1 16:10:03
Proprio per questo motivo è possibile tornare indietro e annullare il drop, facendo un "flashback drop". Il comando è FLASHBACK TABLE… TO BEFORE DROP, e come scaltramente starete pensando, non fa altro che riportare la tabella al nome originale :
SQL> flashback table tst to before drop; Flashback complete. SQL> select * from tst ; COL ROW_CHNG ---------- -------- Version1 16:10:03 SQL> select * from recyclebin ; no rows selected
Proprio per questo se droppiamo una tabella anche di dimensioni sensibili non vedremo liberarsi spazio nel tablespace. Di fatto i dati non si sono mossi da dov'erano, e non lo faranno finchè non decideremo di eliminare definitivamente la tabella dal nostro RB. Per farlo basta il comando PURGE. Vedete nell'esempio come dopo il purge l'oggetto non compaia piu nel RB, il che vuol dire che è stato definitivamente eliminato, e quindi non più ripristinabile.
SQL> select object_name, original_name, type, can_undrop as "UND", can_purge as "PUR", droptime 2 from recyclebin SQL> / OBJECT_NAME ORIGINAL_NAME TYPE UND PUR DROPTIME ------------------------------ ------------- ------------------------- --- --- ------------------- BIN$HGnc55/7rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:10:12 SQL> purge table "BIN$HGnc55/7rRPgQPeM/qQoRw==$0" ; Table purged. SQL> select * from recyclebin ; no rows selected
Ma proseguiamo...
Ci sono vari modi di fare il purge. facendo PURGE RECYCLEBIN si svuota il recyclebin dell'utente (ricordate il sinonimo RECYCLEBIN corrisponde a USER_RECYCLEBIN), mentre se siamo dba possiamo fare PURGE DBA_RECYCLEBIN e cancellare tutto il cancellabile, in un impeto di megalomania.
Se non sappiamo o non possiamo disattivare il recyclebin, ma abbiamo comunque bisogno di sfruttare al massimo il nostro tablespace o la nostra quota utente, il RB ci viene in aiuto, per quello che può. In sostanza, quando abbiamo finito lo spazio, il RB comincia a cancellare gli oggetti a partire dai meno recenti, quel tanto che basta per far spazio ai nuovi oggetti e dati che stiamo scrivendo. Se il nostro tablespace è AUTOEXTEND ON, ovvero si autoestende, prima di estendere un datafile il RB cancellerà degli oggetti. Il che vuol dire che l'ingombro del cestino non influirà sulla larghezza massima del tablespace, anche se è autoestendibile.
...e se cancello più volte una tabella con lo stesso nome?
A parte il fatto che probabilmente se lo stai facendo sei un po confuso, proviamo a farlo:
SQL> create table tst (col varchar2(10), row_chng_dt date); Table created. SQL> insert into tst values ('Version1', sysdate); 1 row created. SQL> drop table tst; Table dropped. SQL> create table tst (col varchar2(10), row_chng_dt date); Table created. SQL> insert into tst values ('Version2', sysdate); 1 row created. SQL> drop table tst; Table dropped. SQL> select object_name, original_name, type, can_undrop as "UND", can_purge as "PUR", droptime 2 from recyclebin; OBJECT_NAME ORIGINAL_NAME TYPE UND PUR DROPTIME ------------------------------ ------------- ----- --- --- ------------------- BIN$HGnc55/7rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:10:12 BIN$HGnc55/8rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:19:53 Query the two dropped tables to verify that they are different: SQL> select * from "BIN$HGnc55/7rRPgQPeM/qQoRw==$0"; COL ROW_CHNG ---------- -------- Version1 16:10:03 SQL> select * from "BIN$HGnc55/8rRPgQPeM/qQoRw==$0" ; COL ROW_CHNG ---------- -------- Version2 16:19:45
Come vedete dai risultati interrogando il RB otteniamo due tabelle droppate, a orari differenti ovviamente. Se facciamo un FLASHBACK DROP per la tabella TST, quale versione ripristinerà Oracle?
SQL> flashback table tst to before drop;
Flashback complete.
SQL> select * from tst;
COL ROW_CHNG
---------- --------
Version2 16:19:45
Oracle ripristinerà sempre la versione più recente dell'oggetto droppato.
Per ripristinare una versione meno recente dell'ultima droppata, possiamo rilanciare il comando di flashback drop finchè non otteniamo la versione che ci interessa, oppure possiamo lanciare il comando sulla versione esatta da ripristinare. Droppiamo di nuovo due volte la tabella e ne ripristiniamo dal RB la versione meno recente, chiamandola per nome (occhio cambia solo /7 e /9 tra i nomi delle due versioni):
SQL> drop table tst;
Table dropped.
SQL> select object_name, original_name, type, can_undrop as "UND", can_purge as "PUR", droptime
2 from recyclebin;
OBJECT_NAME ORIGINAL_NAME TYPE UND PUR DROPTIME
------------------------------ ------------- ------ --- --- -------------------
BIN$HGnc55/7rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:10:12
BIN$HGnc55/9rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:21:00
To flashback to the first version, refer to the BIN$... name of the first version of TST:
SQL> flashback table "BIN$HGnc55/7rRPgQPeM/qQoRw==$0" to before drop;
Flashback complete.
SQL> select * from tst;
COL ROW_CHNG
---------- --------
Version1 16:10:03
Cosi facendo la versione meno recente è stata ripristinata in TST e non è più presente nel RB, mentre è invece presente la seconda tabella droppata, che non abbiamo flashbackato.
SQL> select object_name, original_name, operation, can_undrop as "UND", can_purge as "PUR", droptime 2 from recyclebin; OBJECT_NAME ORIGINAL_NAME OPERATION UND PUR DROPTIME ------------------------------ -------------- --------- --- --- ------------------- BIN$HGnc55/9rRPgQPeM/qQoRw==$0 TST DROP YES YES 2006-09-01:16:21:00
Oggetti Dipendenti
Ovviamente droppando una tabella, droppiamo anche tutti gli oggetti dipendenti da essa(indici, constraints, triggers, ecc). Con il RB abilitato, droppando la tabella, Oracle la rinomina insieme a tutti gli oggetti correlati, mantenendo intatte le relazioni tra essi. I trigger e gli indici ad esempio vengono modificati per puntare al nuovo nome della tabella droppata (quello che comincia con BIN$), mentre le stored procedure verranno invalidate
Ad esempio, aggiungiamo un indice, droppiamo la tabella e vediamo cosa troviamo nel cestino:
SQL> truncate table tst; Table truncated. SQL> insert into tst values ('Version3', sysdate); 1 row created. SQL> create index ind_tst_col on tst(col); Index created. SQL> select * from tst; COL ROW_CHNG ---------- -------- Version3 16:26:10 SQL> drop table tst ; Table dropped. SQL> select object_name, original_name, type, can_undrop as "UND", can_purge as "PUR", droptime 2 from recyclebin 3 order by droptime; OBJECT_NAME ORIGINAL_NAME TYPE UND PUR DROPTIME ------------------------------ -------------- ------ --- --- ------------------- BIN$HGnc55/9rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:21:00 BIN$HGnc55//rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:27:36 BIN$HGnc55/+rRPgQPeM/qQoRw==$0 IND_TST_COL INDEX NO YES 2006-09-01:16:27:36 The RECYCLEBIN views have a few other columns that make the relationship between TST and IND_TST_COL clear: SQL> select object_name, original_name, type, can_undrop as "UND", 2 can_purge as "PUR", droptime, base_object, purge_object 3 from recyclebin 4 order by droptime; OBJECT_NAME ORIGINAL_NAME TYPE UND PUR DROPTIME BASE_OBJECT PURGE_OBJECT ------------------------------ --------------- ----- --- --- ------------------- ----------- ------------ BIN$HGnc55/9rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:21:00 233032 233032 BIN$HGnc55//rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:27:36 233031 233031 BIN$HGnc55/+rRPgQPeM/qQoRw==$0 IND_TST_COL INDEX NO YES 2006-09-01:16:27:36 233031 233434
Troviamo quindi nel RB anche l'indice questa volta, rinominato. Se flashbackiamo la nostra tabella TST, noteremo che anche l'indice viene ripristinato, ma Oracle non lo rinomina col nome originale, e lo lascia col nome che aveva nel RB, ovvero BIN$xxx, come possiamo vedere:
SQL> flashback table tst to before drop; Flashback complete. SQL> select * from tst ; COL ROW_CHNG ---------- -------- Version3 16:26:10 SQL> select index_name from user_indexes where table_name='TST' ; INDEX_NAME ------------------------------ BIN$HGnc55/+rRPgQPeM/qQoRw==$0
Se droppiamo di nuovo la tabella TST, l'indice verrà droppato con il nome originale cambiato, ovvero quello con cui era nominato nel RB quando era stato già droppato in precedenza, e già qui abbiamo fatto un bel casotto, ma ripeto siamo sempre nella zona dormitopoco/ètardi/acidi, per cui è giusto anche cosi:
SQL> drop table tst; Table dropped. SQL> select object_name, original_name, type, can_undrop as "UND", can_purge as "PUR", 2 droptime, base_object, purge_object 3 from recyclebin 4 order by droptime; OBJECT_NAME ORIGINAL_NAME TYPE UND PUR DROPTIME BASE_OBJECT PURGE_OBJECT ------------------------------ --------------- ----- --- --- ------------------- ----------- ------------ BIN$HGnc55/9rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:21:00 233032 233032 BIN$HGnc56ABrRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:31:43 233031 233031 BIN$HGnc56AArRPgQPeM/qQoRw==$1 BIN$HGnc55/+rRP INDEX NO YES 2006-09-01:16:31:43 233031 233434 gQPeM/qQoRw==$0
Notate il campo CAN_UNDROP e CAN_PURGE, riportarti per praticità come UND E PUR. Un indice non può essere flashbackato senza la sua tabella, per cui non lo si può flashbackare se non flashbackando la sua tabella. Se ne può invece fare il purge separatamente dalla tabella.
Vediamo come facendo il purge del solo indice e ripristinando la tabella, non troviamo più l'indice:
SQL> purge index "BIN$HGnc56AArRPgQPeM/qQoRw==$1" ;
Index purged.
SQL> select object_name, original_name, type, can_undrop as "UND", can_purge as "PUR",
2 droptime, base_object, purge_object
3 from recyclebin
4 order by droptime;
OBJECT_NAME ORIGINAL_NAME TYPE UND PUR DROPTIME BASE_OBJECT PURGE_OBJECT
------------------------------ -------------- ----- --- --- ------------------- ----------- ------------
BIN$HGnc55/9rRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:21:00 233032 233032
BIN$HGnc56ABrRPgQPeM/qQoRw==$0 TST TABLE YES YES 2006-09-01:16:31:43 233031 233031
Now, if we restore the table, it will be restored without the index:
SQL> flashback table tst to before drop;
Flashback complete.
SQL> select * from tst ;
COL ROW_CHNG
---------- --------
Version3 16:26:10
SQL> select index_name from user_indexes where table_name='TST' ;
no rows selected
Per quanto riguarda i segmenti LOB, quelli creati ad esempio con i campi BLOB E CLOB, il procedimento del RB è sempre lo stesso, salvo che non se ne può fare il purge singolarmente. Sono ripristinati e cancellati sempre e solo in seguito al flashback o al purge della tabella di appartenenza.
Limitazioni
Ci sono delle limitazioni da tener presente, smanettando con l'RB.
Gli Indici Bitmap non finiscono nel RB, per cui se li avete droppati sono cancellati, e se ripristinate la tabella di origine non ve li ritroverete.
Stessa cosa per i logs delle viste materializzate, e per le foreign keys verso altre tabelle, quando droppate la tabella ve le perdete.
Se per problemi di spazio il RB comincia a fare il purge degli oggetti più vecchi, a parità di posizione, droppa prima gli indici. Ovviamente se ripristinate una tabella i cui indici sono stati droppati, verrà ripristinata senza indici...
Disabilitare il Recyblebin
Per non usare il RB in una singola drop, basta specificare l'opzione PURGE alla fine del drop, e l'oggetto verrà definitivamente cancellato senza passare dal RB:
SQL> purge recyclebin; Recyclebin purged. SQL> select * from recyclebin; no rows selected SQL> create table my_new_table (dummy varchar2(1)); Table created. SQL> drop table my_new_table purge; Table dropped. SQL> select * from recyclebin; no rows selected
Se disabilitiamo il RB a livello di sessione, con ALTER SESSION SET RECYCLEBIN=OFF, è la stessa cosa di fare ciascun drop mettendo il PURGE alla fine, per l'intera sessione o finchè non lo riattiviamo. Nulla ci impedisce comunque di ripristinare oggetti messi nel RB prima che lo disabilitassimo.
In conclusione...
Che dire, sicuramente il recyclebin è uno strumento utile che ci mette una rete di sicurezza in più ai casini che possiamo fare nella fretta o nella disattenzione del momento. E' anche vero che ripristinando tabelle con indici e trigger dovremo ripassare a rinominare, ma almeno i dati sono salvi, ed è quello l'importante.
Certo è che, come i classici avvisi di conferma di cancellazione, se clicchiamo più volte senza leggere non c'è sistema che ci possa salvare se non li backup, quando c'è e quando è possibile ripristinarlo.
Mi è capitato di droppare l'intero schema SYSTEM di un cliente una volta, semplicemente per aver inavvertitamente scrollato con la rotella del mouse sul selettore delle connessioni del tool che utilizzavo per gestire il db, e quando me ne sono accorto ci mancava giusto mezzo schema di roba, per cui... parlo con cognizione di causa ;)
Nessun commento:
Posta un commento