Databasereplicatie is niet langer beperkt tot Oracle-naar-Oracle-configuraties; Oracle-to-cloud en Oracle-to-BigQuery zijn slechts twee van de verschillende opties die nu kunnen worden geselecteerd voor replicatieconfiguraties. In een groot aantal van deze configuraties is GoldenGate het instrument bij uitstek, gezien zijn veelzijdigheid en betrouwbaarheid. Helaas kunnen acties, zoals tafelaanpassingen, bij het repliceren van Oracle naar een ander platform een aap in de war gooien. Het zou dus wenselijk zijn om dergelijke veranderingen bij te houden in afwachting van het gracieus en snel verwerken van GoldenGate-extracten. Laten we naar de mogelijke scenario's kijken en de beste manier van handelen bepalen.
De eerste gedachte die de DBA zou kunnen hebben, is Unified Auditing, omdat het een schat aan informatie biedt voor controleerbare acties. Helaas staat 'controletabel' niet in de lijst met beschikbare privileges om te controleren:
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 >
Interessant is dat het 'ALTER ELKE TABLE'-privilege is controleerbaar, maar het controleert niet wat u zou denken dat zou worden gecontroleerd:
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 >
Een dergelijk beleid controleert alleen het verlenen van dergelijke rechten aan andere gebruikers en levert mogelijk niet altijd een controleverslag op. Aan deze eis wordt nog niet voldaan door auditing, dus moet een andere oplossing worden geproduceerd. Gelukkig biedt Oracle triggers op systeemniveau die auditrecords voor dergelijke acties kunnen produceren. Hieronder ziet u een voorbeeld van hoe dit zou kunnen. Eerst wordt een tabel gemaakt om de gegenereerde auditrecords te bevatten:
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);
De tabel is geïndexeerd op obj_owner en operatie om het genereren van rapporten te versnellen. Vervolgens wordt een trigger gemaakt als de gebruiker die eigenaar is van de tabellen die moeten worden gecontroleerd om alle CREATE-, ALTER- en DROP-statements die zijn uitgevoerd te loggen:
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; /
Aangezien het aantal 64-byte ‘pieces’ van de SQL-tekst behoorlijk groot kan zijn, beperkt de trigger de SQL_TEXT-kolom tot de eerste drie ‘pieces’, waardoor de maximale lengte van de string 192 karakters is. Zoals verwacht voor grotere uitspraken zal de volledige tekst niet worden verstrekt, maar het moet alle 'tabel wijzigen'-instructies in hun geheel bevatten. Houd er rekening mee dat deze trigger niet alleen ALTER TABLE-instructies vastlegt, maar ook alle CREATE/ALTER/DROP-instructies die naar de database zijn verzonden. Dit betekent dat gebruiker wijzigen, trigger wijzigen, pakket wijzigen, functie wijzigen, tabelruimte wijzigen, systeem wijzigen, maken … en neerzetten … instructies ook vastleggen in de DDL_LOG-tabel. Hierdoor kan de tafel snel groeien en behoorlijk groot worden, dus moet er een plan worden gemaakt om een eindige geschiedenis bij te houden. Voor de meeste systemen zouden 90 dagen voldoende moeten zijn om tabelwijzigingen in de database bij te houden. Rapporten die zijn gegenereerd op basis van de gelogde gegevens kunnen voor langere tijd worden bewaard (bijvoorbeeld 12 maanden) voordat ze worden verwijderd.
Hieronder vindt u een voorbeeldscript voor het beheren van de tabelgegevens; het dwingt een gegevensvenster van 90 dagen af. Er wordt een logmap aangemaakt:
mkdir -p /u01/app/oracle/ddl_chg/purge_logs
Er is een SQL-script geschreven om de oude records uit DDL_LOG te verwijderen:
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
Dit kan natuurlijk niet rechtstreeks vanuit cron (of een vergelijkbare planner) worden uitgevoerd, dus een wrapper-script is nodig:
#!/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 {} ;
Het shellscript stelt de juiste omgeving en ORACLE_SID in op basis van de uitvoer van het ps-commando. Het script moet worden bewerkt om de databasenaam op te geven waarnaar moet worden gezocht en de ORACLE_HOME-locatie. U kunt meer dan één databasenaam opgeven met | als scheidingsteken:
'abd|def|ghi|jkl'
Dit biedt een manier om de DDL_LOG-tabel op te schonen in elke database waarin deze tabel/trigger-combinatie is geïnstalleerd. De databasenaam is opgenomen in de naam van het logbestand om de zuiveringssporen voor elke database gescheiden te houden. De tijdsduur voor het bewaren van de logbestanden kan worden gewijzigd om te voldoen aan de opslaglimieten van het systeem dat wordt bewaakt.
Wijzigingsrapporten kunnen worden gegenereerd op basis van de gegevens in de DDL_LOG-tabel:
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
De databasenaam wordt doorgegeven aan het script, zodat deze wordt opgenomen in de bestandsnaam van het rapport. De code rapporteert alleen over tabelwijzigingen (vandaar de lange reeks UNION-query's) en produceert een rapport dat lijkt op het onderstaande:
TAB_NAME MODIFICATION MOD_TIME ---------------- ------------------------------ ----------------------------- SCOTT.DDL_LOG modify sql_text varchar2(200) 23-NOV-19 01.23.49.859971 PM
Het script stelt ook de kolomopmaak in op basis van de maximale lengte van de opgeslagen gegevens om de regellengte mogelijk te verkleinen. Tijdstempelgegevens werden gebruikt om zowel datum- als zichtbare tijdwaarden te bieden voor de gegenereerde wijzigingsrecords. Deze scripts zijn getest, maar vereisen mogelijk enkele aanpassingen op basis van de implementatie van Linux/Unix door de leverancier van het besturingssysteem.
Voor die DBA's die geen gerepliceerde systemen draaien, heeft dit misschien niet veel zin. Maar voor degenen die gegevens van Oracle naar andere systemen repliceren (zoals BigQuery, Snowflake en dergelijke), kan het gemakkelijker worden om te gaan met replicatiefouten die door die wijzigingen zijn veroorzaakt door te weten wanneer tabelwijzigingen hebben plaatsgevonden. Hoe sneller het replicatieproces weer op het goede spoor komt, hoe sneller de systemen die op die gerepliceerde gegevens vertrouwen, weer functioneel kunnen worden.
# # #
Zie artikelen van David Fitzjarrell