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