sql >> Database >  >> RDS >> Oracle

Tabelwijziging in Oracle volgen

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


  1. Verbinding maken met meerdere SQL-servers in één klik (geregistreerde serversgroep) - SQL Server / TSQL-zelfstudie, deel 5

  2. Hoe verwijder ik uit meerdere tabellen met INNER JOIN in SQL server?

  3. Deelnemen aan meerdere kolommen

  4. Best practice:importeer mySQL-bestand in PHP; gesplitste zoekopdrachten