Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Überwachen von Tabellenänderungen in Oracle

Die Datenbankreplikation ist nicht mehr auf Oracle-zu-Oracle-Konfigurationen beschränkt; Oracle-to-Cloud und Oracle-to-BigQuery sind nur zwei der verschiedenen Optionen, die jetzt für Replikationskonfigurationen ausgewählt werden können. In vielen dieser Konfigurationen ist GoldenGate aufgrund seiner Vielseitigkeit und Zuverlässigkeit das Tool der Wahl. Leider können beim Replizieren von Oracle auf eine andere Plattform Aktionen wie Tabellenänderungen einen Affenschlüssel in die Arbeit werfen. Daher wäre es wünschenswert, solche Änderungen in Erwartung einer ordnungsgemäßen und schnellen Behandlung von GoldenGate-Extraktabbrüchen zu verfolgen. Sehen wir uns die möglichen Szenarien an und bestimmen die beste Vorgehensweise.

Der erste Gedanke, den der DBA haben könnte, ist Unified Auditing, da es eine Fülle von Informationen für überprüfbare Aktionen bereitstellt. Leider ist „Audit-Tabelle“ nicht in der Liste der verfügbaren Audit-Privilegien enthalten:

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 >

Interessanterweise ist das Privileg „ALTER ANY TABLE“ überprüfbar, aber es wird nicht geprüft, was Sie vielleicht für eine Prüfung halten:

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 > 

Eine solche Richtlinie prüft nur die Gewährung solcher Privilegien an andere Benutzer und erzeugt möglicherweise nicht immer einen Prüfdatensatz. Die Anforderung wird durch die Auditierung noch nicht erfüllt, so dass eine andere Lösung hergestellt werden muss. Glücklicherweise bietet Oracle Trigger auf Systemebene an, die Audit-Aufzeichnungen für solche Aktionen erstellen können. Ein Beispiel dafür, wie dies geschehen könnte, ist unten gezeigt. Zunächst wird eine Tabelle erstellt, die die generierten Audit-Datensätze enthält:

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

Die Tabelle wird auf obj_owner und Operation indiziert, um die Berichterstellung zu beschleunigen. Als nächstes wird ein Trigger als Benutzer erstellt, dem die zu überwachenden Tabellen gehören, um alle CREATE-, ALTER- und DROP-Anweisungen zu protokollieren, die ausgeführt wurden:

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

Da die Anzahl der 64-Byte-„Teile“ des SQL-Texts ziemlich groß sein kann, beschränkt der Trigger die Spalte SQL_TEXT auf die ersten drei „Teile“, wodurch die maximale Länge der Zeichenfolge 192 Zeichen beträgt. Wie bei größeren Anweisungen zu erwarten, wird der vollständige Text nicht bereitgestellt, aber er sollte alle „Tabelle ändern“-Anweisungen in ihrer Gesamtheit erfassen. Beachten Sie, dass dieser Trigger nicht nur ALTER TABLE-Anweisungen erfasst, sondern auch alle an die Datenbank übermittelten CREATE/ALTER/DROP-Anweisungen. Das bedeutet, dass die Anweisungen alter user, alter trigger, alter package, alter function, alter tablespace, alter system, create … und drop … ebenfalls in der Tabelle DDL_LOG protokolliert werden. Aus diesem Grund kann die Tabelle schnell wachsen und ziemlich groß werden, daher sollte ein Plan zum Aufbewahren einer endlichen Historie erstellt werden. Für die meisten Systeme sollten 90 Tage ausreichen, um Tabellenänderungen in der Datenbank nachzuverfolgen. Aus den protokollierten Daten generierte Berichte können für längere Zeit (z. B. 12 Monate) aufbewahrt werden, bevor sie entfernt werden.

Ein Beispielskript zum Verwalten der Tabellendaten wird unten bereitgestellt; es erzwingt ein 90-tägiges Datenfenster. Ein Protokollverzeichnis wird erstellt:

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

Ein SQL-Skript wird geschrieben, um die alten Datensätze aus DDL_LOG zu löschen:

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

Dies kann natürlich nicht direkt von Cron (oder einem ähnlichen Planer) ausgeführt werden, daher wird ein Wrapper-Skript benötigt:

#!/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 {} ;

Das Shell-Skript legt die richtige Umgebung und ORACLE_SID basierend auf der Ausgabe des Befehls ps fest. Das Skript muss bearbeitet werden, um den zu suchenden Datenbanknamen und den Speicherort ORACLE_HOME anzugeben. Mit | können mehrere Datenbanknamen angegeben werden als Trennzeichen:

'abd|def|ghi|jkl'

Dies bietet eine Möglichkeit, die Tabelle DDL_LOG in jeder Datenbank zu löschen, in der diese Kombination aus Tabelle und Trigger installiert wurde. Der Datenbankname ist im Protokolldateinamen enthalten, um die Bereinigungspfade für jede Datenbank getrennt zu halten. Die Dauer der Aufbewahrung der Protokolldateien kann geändert werden, um die Speichergrenzen des überwachten Systems einzuhalten.

Änderungsberichte können aus den in der Tabelle DDL_LOG gefundenen Daten generiert werden:

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

Der Datenbankname wird an das Skript übergeben, sodass er im Namen der Berichtsdatei enthalten ist. Der Code meldet nur Tabellenänderungen (daher die lange Zeichenfolge von UNION-Abfragen) und erzeugt einen Bericht ähnlich dem unten gezeigten:

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

Das Skript setzt auch die Spaltenformatierung basierend auf der maximalen Länge der gespeicherten Daten, um eventuell die Zeilenlänge zu reduzieren. Zeitstempeldaten wurden verwendet, um sowohl Datums- als auch sichtbare Zeitwerte für die generierten Änderungsdatensätze bereitzustellen. Diese Skripte wurden getestet, erfordern jedoch möglicherweise einige Änderungen basierend auf der Linux/Unix-Implementierung des Betriebssystemanbieters.

Für DBAs, die keine replizierten Systeme ausführen, ist dies möglicherweise nicht von großem Nutzen. Aber für diejenigen, die Daten von Oracle auf andere Systeme (wie BigQuery, Snowflake und dergleichen) replizieren, kann das Wissen, wann Tabellenänderungen aufgetreten sind, den Umgang mit Replikationsfehlern erleichtern, die durch diese Änderungen verursacht wurden. Je schneller der Replikationsprozess wieder in Gang kommt, desto schneller können die Systeme, die sich auf diese replizierten Daten verlassen, wieder funktionieren.

# # #

Siehe Artikel von David Fitzjarrell