Databasreplikering är inte längre begränsad till Oracle-till-Oracle-konfigurationer; Oracle-to-cloud och Oracle-to-BigQuery är bara två av de olika alternativen som nu kan väljas för replikeringskonfigurationer. I ett stort antal av dessa konfigurationer ligger GoldenGate som det bästa verktyget, med tanke på dess mångsidighet och tillförlitlighet. Tyvärr, när du replikerar Oracle till en annan plattform, kan åtgärder som tabelländringar kasta en skiftnyckel i arbetet. Därför skulle det vara önskvärt att spåra sådana förändringar i väntan på hantering av GoldenGate-extraktet abends graciöst och snabbt. Låt oss titta på de möjliga scenarierna och bestämma det bästa tillvägagångssättet.
Den första tanken som DBA kan ha är Unified Auditing, eftersom det ger en mängd information för granskningsbara åtgärder. Tyvärr finns inte "revisionstabell" på listan över tillgängliga privilegier att granska:
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 >
Intressant nog är-privilegiet "ÄNDRA ALLA TABELL". kan granskas, men den granskar inte vad du kanske tror skulle granskas:
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 >
En sådan policy granskar bara beviljandet av sådana privilegier till andra användare och kanske inte alltid producerar en revisionspost. Kravet är ännu inte uppfyllt genom revision så en annan lösning måste tas fram. Lyckligtvis erbjuder Oracle utlösare på systemnivå som kan producera revisionsposter för sådana åtgärder. Ett exempel på hur detta kan göras visas nedan. Först skapas en tabell som innehåller de genererade granskningsposterna:
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);
Tabellen indexeras på obj_owner och operation för att påskynda rapportgenereringen. Därefter skapas en utlösare som användaren som äger tabellerna som ska övervakas för att logga alla CREATE-, ALTER- och DROP-satser som har körts:
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; /
Eftersom antalet 64-byte 'bitar' av SQL-texten kan vara ganska stort begränsar triggern SQL_TEXT-kolumnen till de första tre 'bitarna', vilket gör att strängens maximala längd är 192 tecken. Som förväntat för större påståenden kommer inte den fullständiga texten att tillhandahållas men den bör fånga alla "ändringstabell"-påståenden i sin helhet. Observera att denna utlösare inte bara kommer att fånga ALTER TABLE-satser utan även alla CREATE/ALTER/DROP-satser som skickas till databasen. Detta innebär att ändra användare, ändra trigger, ändra paket, ändra funktion, ändra tabellutrymme, ändra system, skapa ... och släppa ... uttalanden också loggas i DDL_LOG tabellen. På grund av detta kan bordet växa snabbt och bli ganska stort, därför bör en plan för att behålla en ändlig historia skapas. För de flesta system bör 90 dagar vara tillräckligt för att spåra tabelländringar i databasen. Rapporter som genereras från loggade data kan sparas under längre perioder (till exempel 12 månader) innan de tas bort.
Ett exempelskript för att hantera tabelldata finns nedan; det upprätthåller ett datafönster på 90 dagar. En loggkatalog skapas:
mkdir -p /u01/app/oracle/ddl_chg/purge_logs
Ett SQL-skript skrivs för att rensa de gamla posterna från 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
Detta kan uppenbarligen inte köras direkt från cron (eller liknande schemaläggare) så ett omslagsskript behövs:
#!/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 {} ;
Skalskriptet ställer in rätt miljö och ORACLE_SID baserat på utdata från ps-kommandot. Skriptet måste redigeras för att ange databasnamnet att söka efter och ORACLE_HOME-platsen. Mer än ett databasnamn kan anges med | som en separator:
'abd|def|ghi|jkl'
Detta ger ett sätt att rensa DDL_LOG-tabellen i varje databas där denna tabell/trigger-kombination har installerats. Databasnamnet ingår i loggfilens namn för att hålla rensningsspåren separata för varje databas. Tidslängden för att behålla loggfilerna kan ändras för att möta lagringsgränserna för det system som övervakas.
Ändringsrapporter kan genereras från data som finns i tabellen 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
Databasnamnet skickas till skriptet så att det inkluderas i rapportfilnamnet. Koden rapporterar endast om tabelländringar (därav den långa strängen av UNION-frågor) och producerar en rapport som liknar den som visas nedan:
TAB_NAME MODIFICATION MOD_TIME ---------------- ------------------------------ ----------------------------- SCOTT.DDL_LOG modify sql_text varchar2(200) 23-NOV-19 01.23.49.859971 PM
Skriptet ställer också in kolumnformateringen baserat på den maximala längden på lagrad data för att eventuellt minska radlängden. Tidstämpeldata användes för att tillhandahålla både datum och synliga tidsvärden för de genererade ändringsposterna. Dessa skript har testats men kan kräva vissa modifieringar baserat på operativsystemleverantörens implementering av Linux/Unix.
För de DBA:er som inte kör replikerade system kanske detta inte är till stor nytta. Men för de som replikerar data från Oracle till andra system (som BigQuery, Snowflake och liknande), kan det att veta när tabelländringar har inträffat göra det lättare att hantera replikeringsfel som skapats av dessa ändringar. Ju snabbare replikeringsprocessen kan komma tillbaka på rätt spår desto snabbare kan de system som förlitar sig på den replikerade data återgå till funktionalitet.
# # #
Se artiklar avDavid Fitzjarrell