Oracle
 sql >> Database >  >> RDS >> Oracle

Monitoraggio della modifica della tabella in Oracle

La replica del database non è più limitata alle configurazioni da Oracle a Oracle; Oracle-to-cloud e Oracle-to-BigQuery sono solo due delle varie opzioni che ora possono essere selezionate per le configurazioni di replica. In un buon numero di queste configurazioni GoldenGate è lo strumento preferito, data la sua versatilità e affidabilità. Sfortunatamente, durante la replica di Oracle su un'altra piattaforma, azioni come le modifiche alle tabelle possono lanciare una chiave inglese nel lavoro. Pertanto, sarebbe desiderabile tenere traccia di tali cambiamenti in previsione della gestione degli estratti di GoldenGate con grazia e rapidità. Diamo un'occhiata ai possibili scenari e determiniamo la migliore linea d'azione.

Il primo pensiero che il DBA potrebbe avere è il controllo unificato, in quanto fornisce una vasta gamma di informazioni per le azioni verificabili. Purtroppo "tabella di controllo" non è nell'elenco dei privilegi disponibili per il controllo:

SCOTT @ orcl > create audit policy alter_tab_pol
  2  privileges alter table;
privileges alter table
           *
ERROR at line 2:
ORA-46355: missing or invalid privilege audit option.


SCOTT @ orcl >

È interessante notare che il privilegio "ALTER ANY TABLE" è verificabile, ma non verifica ciò che potresti pensare verrebbe verificato:

SCOTT @ orcl > create audit policy table_pol
  2  privileges create any table, alter any table, drop any table;

Audit policy created.

SCOTT @ orcl > audit policy table_pol;

Audit succeeded.

SCOTT @ orcl > 

Tale criterio controlla solo la concessione di tali privilegi ad altri utenti e potrebbe non produrre sempre un record di controllo. Il requisito non è ancora soddisfatto dall'audit, quindi è necessario produrre un'altra soluzione. Fortunatamente, Oracle offre trigger a livello di sistema che possono produrre record di controllo per tali azioni. Un esempio di come ciò potrebbe essere fatto è mostrato di seguito. Per prima cosa viene creata una tabella per contenere i record di controllo generati:

create table ddl_log (
operation   varchar2(30),
obj_owner   varchar2(35),
object_name varchar2(35),
sql_text    varchar2(200),
attempt_by  varchar2(35),
attempt_dt  timestamp);
 
create index ddl_log_idx 
on ddl_log(obj_owner, operation);

La tabella è indicizzata su obj_owner e sull'operazione per accelerare la generazione dei report. Successivamente viene creato un trigger come l'utente che possiede le tabelle da monitorare per registrare tutte le istruzioni CREATE, ALTER e DROP che sono state eseguite:

create or replace trigger ddl_trigger
before create or alter or drop
on schema

declare
 oper ddl_log.operation%type;
 sql_text ora_name_list_t;
 i        pls_integer; 
begin
  i := sql_txt(sql_text);
  if i = 1 then
        insert into ddl_log
        select ora_sysevent, ora_dict_obj_owner,
        ora_dict_obj_name, sql_text(1), user, v_systimestamp
        from dual;
  elsif i = 2 then
        insert into ddl_log
        select ora_sysevent, ora_dict_obj_owner,
        ora_dict_obj_name, sql_text(1)||sql_text(2), user, v_systimestamp
        from dual;
  elsif i >= 3 then
        insert into ddl_log
        select ora_sysevent, ora_dict_obj_owner,
        ora_dict_obj_name, sql_text(1)||sql_text(2)||sql_text(3), user, v_systimestamp
        from dual;
  end if;

end ddl_trigger;
/

Poiché il numero di "pezzi" di 64 byte del testo SQL può essere piuttosto grande, il trigger limita la colonna SQL_TEXT ai primi tre "pezzi", rendendo la lunghezza massima della stringa 192 caratteri. Come previsto per affermazioni più grandi, il testo completo non verrà fornito ma dovrebbe catturare tutte le affermazioni "alter table" nella loro interezza. Si noti che questo trigger acquisirà non solo le istruzioni ALTER TABLE ma anche qualsiasi istruzione CREATE/ALTER/DROP inviata al database. Ciò significa che le istruzioni alter user, alter trigger, alter package, alter function, alter tablespace, alter system, create … e drop … vengono registrate anche nella tabella DDL_LOG. Per questo motivo, il tavolo può crescere rapidamente e diventare piuttosto grande, quindi dovrebbe essere creato un piano per mantenere una storia finita. Per la maggior parte dei sistemi, 90 giorni dovrebbero essere sufficienti per tenere traccia delle modifiche alle tabelle nel database. I rapporti generati dai dati registrati possono essere conservati per periodi di tempo più lunghi (ad esempio 12 mesi) prima di essere rimossi.

Di seguito viene fornito uno script di esempio per la gestione dei dati della tabella; impone una finestra di dati di 90 giorni. Viene creata una directory di registro:

mkdir -p /u01/app/oracle/ddl_chg/purge_logs

Viene scritto uno script SQL per eliminare i vecchi record da DDL_LOG:

column sys_date new_value dt noprint
column name new_value db_nm noprint
select to_char(sysdate,'RRRRMMDD') sys_date from dual;
select name from v$database;

spool /u01/app/oracle/ddl_chg/purge_logs/ddl_log_purge_$db_nm._&dt..log
set echo on

--
-- Records slated for removal
--
select * From ddl_log where attempt_dt < sysdate - 90;

--
-- Delete selected records
--
delete from ddl_log where attempt_dt < sysdate - 90;

commit;

spool off
set echo off

Questo, ovviamente, non può essere eseguito direttamente da cron (o qualsiasi altro programmatore simile), quindi è necessario uno script wrapper:

#!/bin/ksh

#
# purge_ddl_log_90.sh
#
# Shell script to purge old audit records
# from the DDL_LOG table
#

#
# Find the selected database and set the environment
#
set -A database `ps -ef | grep [p]mon | grep '<name>' |  awk -F"_" '{print $3}'`

for i in ${database[@]}

#
# Set the environment for the database
#
do
        ORACLE_SID=$i
        export ORACLE_SID

        ORAENV_ASK=NO
        export ORAENV_ASK

        unset ORACLE_BASE
        export ORACLE_BASE

        PATH=$PATH:<ORACLE_HOME/bin location>

        . <ORACLE_HOME/bin>/oraenv -s

        LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib:$ORACLE_HOME/precomp/public
        export LD_LIBRARY_PATH

        PATH=$ORACLE_HOME/bin:$PATH
        export PATH

#
# Start SQL*Plus and execute the script
#
        sqlplus /nolog <<EOF
connect / as sysdba
@/u01/app/oracle/ddl_chg/purge_ddl_log_90.sql
EOF

done

#
# Make the output files readable for all
*
cd /u01/app/oracle/ddl_chg/purge_logs

chmod 666 *.log

#
# Remove old purge logs
#

find . -name "purge*log" -mtime +365 -exec /bin/rm -rf {} ;

Lo script della shell imposta l'ambiente corretto e ORACLE_SID in base all'output del comando ps. Lo script dovrà essere modificato per fornire il nome del database da cercare e la posizione di ORACLE_HOME. È possibile specificare più nomi di database utilizzando | come separatore:

'abd|def|ghi|jkl'

Ciò fornisce un modo per eliminare la tabella DDL_LOG in ogni database in cui è stata installata questa combinazione tabella/trigger. Il nome del database è incluso nel nome del file di registro per mantenere separate le tracce di eliminazione per ciascun database. La durata della conservazione dei file di registro può essere modificata per soddisfare i limiti di archiviazione del sistema monitorato.

I rapporti di modifica possono essere generati dai dati trovati nella tabella DDL_LOG:

set linesize 140
column sdate new_value sdt noprint
select to_Char(sysdate, 'RRRRMMDDHH24')sdate from dual;

column modlen new_value mlen noprint
select 'a'||nvl(max(length(modification)),25) modlen From
(select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'modify ')) modification, attempt_dt mod_time
from ddl_log
where (instr(sql_text, 'alter table') > 0
or instr(sql_text, 'ALTER TABLE') > 0));
column objlen new_value olen noprint
select 'a'||nvl(max(length(owner||'.'||tabname)),60) objlen From
(select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'modify ')) modification, attempt_dt mod_time
from ddl_log
where (instr(sql_text, 'alter table') > 0
or instr(sql_text, 'ALTER TABLE') > 0));

column modification format &mlen
column mod_time format a29
column tab_name format &olen

select owner||'.'|| tabname tab_name, modification, mod_time
from
(select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'add ')) modification, attempt_dt mod_time
from ddl_log
where instr(lower(sql_text), 'alter table') > 0
union
select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'drop ')) modification, attempt_dt mod_time
from ddl_log
where instr(lower(sql_text), 'alter table') > 0
union
select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'modify ')) modification, attempt_dt mod_time
from ddl_log
where instr(lower(sql_text), 'alter table') > 0
union
select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'ADD ')) modification, attempt_dt mod_time
from ddl_log
where instr(lower(sql_text), 'alter table') > 0
union
select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'DROP ')) modification, attempt_dt mod_time
from ddl_log
where instr(lower(sql_text), 'alter table') > 0
union
select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'MODIFY ')) modification, attempt_dt mod_time
from ddl_log
where instr(lower(sql_text), 'alter table') > 0) dl
where lower(dl.modification) not like '%table%'
and mod_time >= trunc(systimestamp)
order by 1, 3

spool /u01/app/oracle/ddl_chg/log/tab_chg_rpt_&sdt._&1..lst
/
spool off

Il nome del database viene passato allo script in modo che venga incluso nel nome del file di report. Il codice riporta solo le modifiche di tabella (da cui la lunga stringa di query UNION) e produce un report simile a quello mostrato di seguito:

TAB_NAME         MODIFICATION                   MOD_TIME
---------------- ------------------------------ -----------------------------
SCOTT.DDL_LOG    modify sql_text varchar2(200)  23-NOV-19 01.23.49.859971 PM

Lo script imposta anche la formattazione della colonna in base alla lunghezza massima dei dati archiviati per ridurre eventualmente la lunghezza della riga. I dati del timestamp sono stati utilizzati per fornire valori di data e ora visibili per i record di modifica generati. Questi script sono stati testati ma potrebbero richiedere alcune modifiche in base all'implementazione di Linux/Unix da parte del fornitore del sistema operativo.

Per quei DBA che non eseguono sistemi replicati questo potrebbe non essere di grande utilità. Tuttavia, per coloro che replicano i dati da Oracle ad altri sistemi (come BigQuery, Snowflake e simili), sapere quando si sono verificate modifiche alla tabella può semplificare la gestione degli errori di replica creati da tali modifiche. Più velocemente il processo di replica può tornare in pista, più velocemente i sistemi che fanno affidamento su quei dati replicati possono tornare alla funzionalità.

# # #

Vedi gli articoli di David Fitzjarrell