sql >> Database teknologi >  >> RDS >> Oracle

Overvågning af tabelændring i Oracle

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


  1. Hvorfor nedsætter MYSQL højere LIMIT offset forespørgslen?

  2. Kan ikke forhindre SQLiteConnection-objektlækage

  3. SQL Server PIVOT måske?

  4. Hvad er LEN() i SQL Server?