Databasereplikering er ikke længere begrænset til Oracle-til-Oracle-konfigurationer; Oracle-to-cloud og Oracle-to-BigQuery er blot to af de forskellige muligheder, der nu kan vælges til replikeringskonfigurationer. I en lang række af disse konfigurationer ligger GoldenGate som det foretrukne værktøj, givet dets alsidighed og pålidelighed. Desværre, når du kopierer Oracle til en anden platform, kan handlinger såsom bordændringer kaste en abe-nøgle i værkerne. Det ville således være ønskeligt at spore sådanne ændringer i forventning om håndtering af GoldenGate-ekstraktet aftager yndefuldt og hurtigt. Lad os se på de mulige scenarier og bestemme den bedste fremgangsmåde.
Den første tanke, som DBA kunne have, er Unified Auditing, da det giver et væld af informationer til reviderbare handlinger. Desværre er 'revisionstabel' ikke blandt listen over tilgængelige privilegier til revision:
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 nok er privilegiet "ÆNDRE ENHVER TABLE". kan revideres, men den reviderer ikke, hvad du måske tror ville blive revideret:
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 politik reviderer kun tildelingen af sådanne privilegier til andre brugere og producerer muligvis ikke altid en revisionspost. Kravet er endnu ikke opfyldt ved revision, så der skal udarbejdes en anden løsning. Heldigvis tilbyder Oracle triggere på systemniveau, som kan producere revisionsposter for sådanne handlinger. Et eksempel på, hvordan dette kan gøres, er vist nedenfor. Først oprettes en tabel til at indeholde de genererede revisionsposter:
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 er indekseret på obj_owner og operation for at fremskynde rapportgenerering. Derefter oprettes en trigger som brugeren, der ejer tabellerne, der skal overvåges, for at logge alle CREATE-, ALTER- og DROP-sætninger, der er blevet udført:
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 antallet af 64-byte 'stykker' af SQL-teksten kan være ret stort, begrænser triggeren SQL_TEXT-kolonnen til de første tre 'stykker', hvilket gør den maksimale længde af strengen 192 tegn. Som forventet for større udsagn vil den komplette tekst ikke blive leveret, men den bør fange alle "ændre tabel"-udsagn i deres helhed. Bemærk, at denne udløser ikke kun vil fange ALTER TABLE-sætninger, men også enhver CREATE/ALTER/DROP-sætning indsendt til databasen. Dette betyder, at ændre bruger, ændre trigger, ændre pakke, ændre funktion, ændre tablespace, ændre system, oprette ... og slippe ... sætninger også bliver logget i DDL_LOG tabellen. På grund af dette kan bordet vokse hurtigt og blive ret stort, og derfor bør der laves en plan for at holde en begrænset historie. For de fleste systemer burde 90 dage være tilstrækkeligt til at spore tabelændringer i databasen. Rapporter genereret fra de loggede data kan opbevares i længere perioder (f.eks. 12 måneder), før de fjernes.
Et eksempelscript til styring af tabeldataene findes nedenfor; det håndhæver et 90-dages datavindue. Der oprettes en log-mappe:
mkdir -p /u01/app/oracle/ddl_chg/purge_logs
Et SQL-script er skrevet for at rense de gamle poster fra 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
Dette kan naturligvis ikke køre direkte fra cron (eller en lignende skemalægger), så et wrapper-script er nødvendigt:
#!/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 {} ;
Shell-scriptet indstiller det korrekte miljø og ORACLE_SID baseret på outputtet af ps-kommandoen. Scriptet skal redigeres for at angive databasenavnet, der skal søges efter, og ORACLE_HOME-placeringen. Mere end ét databasenavn kan angives ved hjælp af | som en separator:
'abd|def|ghi|jkl'
Dette giver en måde at rense DDL_LOG-tabellen i hver database, hvor denne tabel/trigger-kombination er blevet installeret. Databasenavnet er inkluderet i logfilnavnet for at holde udrensningssporene adskilt for hver database. Længden af tid til at opbevare logfilerne kan ændres for at overholde lagergrænserne for det system, der overvåges.
Ændringsrapporter kan genereres ud fra de data, der findes i DDL_LOG-tabellen:
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
Databasenavnet videregives til scriptet, så det vil blive inkluderet i rapportfilnavnet. Koden rapporterer kun om tabelændringer (deraf den lange række af UNION-forespørgsler) og producerer en rapport, der ligner den, der er vist nedenfor:
TAB_NAME MODIFICATION MOD_TIME ---------------- ------------------------------ ----------------------------- SCOTT.DDL_LOG modify sql_text varchar2(200) 23-NOV-19 01.23.49.859971 PM
Scriptet indstiller også kolonneformateringen baseret på den maksimale længde af de lagrede data for muligvis at reducere linjelængden. Tidsstempeldata blev brugt til at give både dato og synlige tidsværdier for de genererede ændringsposter. Disse scripts er blevet testet, men kan kræve nogle ændringer baseret på operativsystemleverandørens implementering af Linux/Unix.
For de DBA'er, der ikke kører replikerede systemer, er dette måske ikke til megen nytte. Men for dem, der replikerer data fra Oracle til andre systemer (såsom BigQuery, Snowflake og lignende), kan det gøre det lettere at håndtere replikeringsfejl, der er skabt af disse ændringer, at vide, hvornår der er sket tabelændringer. Jo hurtigere replikeringsprocessen kan komme tilbage på sporet, jo hurtigere kan de systemer, der er afhængige af de replikerede data, vende tilbage til funktionalitet.
# # #
Se artikler afDavid Fitzjarrell