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