sql >> Databasteknik >  >> RDS >> Oracle

Övervakning av tabellförändringar i Oracle

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


  1. mysql primärnyckel med två kolumner med automatisk ökning

  2. Ändra tabell för att ändra kolumns standardvärde

  3. Hur IF-utlåtandet fungerar i SQL Server

  4. Unikt modellfält i Django och skiftlägeskänslighet (postgres)