sql >> Base de Datos >  >> RDS >> Oracle

Supervisión de cambio de tabla en Oracle

La replicación de bases de datos ya no está restringida a configuraciones de Oracle a Oracle; Oracle a la nube y Oracle a BigQuery son solo dos de las diversas opciones que ahora se pueden seleccionar para las configuraciones de replicación. En un buen número de estas configuraciones se encuentra GoldenGate como herramienta de elección, dada su versatilidad y fiabilidad. Desafortunadamente, al replicar Oracle en otra plataforma, acciones como las modificaciones de la tabla pueden hacer que todo funcione. Por lo tanto, sería deseable realizar un seguimiento de dichos cambios antes de manejar los extractos anómalos de GoldenGate con gracia y rapidez. Veamos los escenarios posibles y determinemos el mejor curso de acción.

El primer pensamiento que podría tener el DBA es la auditoría unificada, ya que proporciona una gran cantidad de información para acciones auditables. Por desgracia, la "tabla de auditoría" no se encuentra entre la lista de privilegios disponibles para auditar:

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 >

Curiosamente, el privilegio 'ALTER ANY TABLE' es auditable, pero no audita lo que podría pensar que sería auditado:

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 > 

Dicha política solo audita la concesión de dichos privilegios a otros usuarios y es posible que no siempre produzca un registro de auditoría. El requisito aún no se ha cumplido con la auditoría, por lo que se debe producir otra solución. Afortunadamente, Oracle ofrece disparadores a nivel de sistema que pueden producir registros de auditoría para tales acciones. A continuación se muestra un ejemplo de cómo se podría hacer esto. Primero se crea una tabla para contener los registros de auditoría generados:

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);

La tabla está indexada en obj_owner y operación para acelerar la generación de informes. A continuación, se crea un activador como usuario propietario de las tablas que se van a supervisar para registrar todas las instrucciones CREATE, ALTER y DROP que se han ejecutado:

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;
/

Dado que el número de "piezas" de 64 bytes del texto SQL puede ser bastante grande, el activador restringe la columna SQL_TEXT a las tres primeras "piezas", lo que hace que la longitud máxima de la cadena sea de 192 caracteres. Como se esperaba para declaraciones más grandes, no se proporcionará el texto completo, pero debe capturar cualquier declaración de "alterar tabla" en su totalidad. Tenga en cuenta que este activador capturará no solo las instrucciones ALTER TABLE, sino también cualquier instrucción CREATE/ALTER/DROP enviada a la base de datos. Esto significa que alterar usuario, alterar activador, alterar paquete, alterar función, alterar espacio de tabla, alterar sistema, crear... y soltar... declaraciones también se registran en la tabla DDL_LOG. Debido a esto, la tabla puede crecer rápidamente y volverse bastante grande, por lo que se debe crear un plan para mantener un historial finito. Para la mayoría de los sistemas, 90 días deberían ser suficientes para realizar un seguimiento de los cambios de tabla en la base de datos. Los informes generados a partir de los datos registrados se pueden conservar durante períodos más prolongados (por ejemplo, 12 meses) antes de eliminarlos.

A continuación se proporciona un script de muestra para administrar los datos de la tabla; impone una ventana de datos de 90 días. Se crea un directorio de registro:

mkdir -p /u01/app/oracle/ddl_chg/purge_logs

Se escribe un script SQL para purgar los registros antiguos de 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

Obviamente, esto no se puede ejecutar directamente desde cron (o cualquier programador similar), por lo que se necesita una secuencia de comandos contenedora:

#!/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 {} ;

El script de shell establece el entorno adecuado y ORACLE_SID en función del resultado del comando ps. Será necesario editar el script para proporcionar el nombre de la base de datos para buscar y la ubicación de ORACLE_HOME. Se puede especificar más de un nombre de base de datos usando | como separador:

'abd|def|ghi|jkl'

Esto proporciona una forma de purgar la tabla DDL_LOG en cada base de datos donde se haya instalado esta combinación de tabla/activador. El nombre de la base de datos se incluye en el nombre del archivo de registro para mantener los rastros de purga separados para cada base de datos. El tiempo de conservación de los archivos de registro se puede cambiar para cumplir con los límites de almacenamiento del sistema que se está supervisando.

Los informes de cambios se pueden generar a partir de los datos que se encuentran en la tabla 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

El nombre de la base de datos se pasa al script para que se incluya en el nombre del archivo del informe. El código informa solo sobre los cambios en la tabla (de ahí la larga cadena de consultas UNION) y genera un informe similar al que se muestra a continuación:

TAB_NAME         MODIFICATION                   MOD_TIME
---------------- ------------------------------ -----------------------------
SCOTT.DDL_LOG    modify sql_text varchar2(200)  23-NOV-19 01.23.49.859971 PM

El script también establece el formato de la columna en función de la longitud máxima de los datos almacenados para reducir posiblemente la longitud de la línea. Los datos de marca de tiempo se utilizaron para proporcionar valores de fecha y hora visibles para los registros de cambios generados. Estos scripts han sido probados pero pueden requerir algunas modificaciones basadas en la implementación de Linux/Unix del proveedor del sistema operativo.

Para aquellos DBA que no ejecutan sistemas replicados, esto podría no ser de mucha utilidad. Pero, para aquellos que replican datos de Oracle a otros sistemas (como BigQuery, Snowflake y similares), saber cuándo se produjeron cambios en la tabla puede facilitar el manejo de las fallas de replicación creadas por esos cambios. Cuanto más rápido pueda volver a encarrilarse el proceso de replicación, más rápido podrán volver a funcionar los sistemas que dependen de esos datos replicados.

###

Ver artículos de David Fitzjarrell