A replicação de banco de dados não está mais restrita a configurações Oracle-to-Oracle; Oracle-to-cloud e Oracle-to-BigQuery são apenas duas das várias opções que agora podem ser selecionadas para configurações de replicação. Em boa parte destas configurações encontra-se o GoldenGate como ferramenta de eleição, dada a sua versatilidade e fiabilidade. Infelizmente, ao replicar o Oracle para outra plataforma, ações como modificações de tabela podem atrapalhar o trabalho. Assim, seria desejável rastrear tais mudanças em antecipação ao tratamento de encerramentos de extração do GoldenGate de forma graciosa e rápida. Vamos analisar os cenários possíveis e determinar o melhor curso de ação.
O primeiro pensamento que o DBA pode ter é a Auditoria Unificada, pois fornece uma riqueza de informações para ações auditáveis. Infelizmente 'tabela de auditoria' não está entre a lista de privilégios disponíveis para auditoria:
SCOTT @ orcl> criar política de auditoria alter_tab_pol 2 privilégios alter table;privileges alter table *ERROR na linha 2:ORA-46355:opção de auditoria de privilégio ausente ou inválida.SCOTT @ orcl>
Curiosamente, o privilégio "ALTER ANY TABLE" é auditável, mas não audita o que você pode pensar que seria auditado:
SCOTT @ orcl> criar política de auditoria table_pol 2 privilégios criar qualquer tabela, alterar qualquer tabela, eliminar qualquer tabela;Política de auditoria criada.SCOTT @ orcl> política de auditoria table_pol;Auditoria bem sucedida.SCOTT @ orcl>
Essa política apenas audita a concessão de tais privilégios a outros usuários e pode nem sempre produzir um registro de auditoria. O requisito ainda não foi atendido pela auditoria, portanto, outra solução deve ser produzida. Felizmente, a Oracle oferece gatilhos em nível de sistema que podem produzir registros de auditoria para tais ações. Um exemplo de como isso pode ser feito é mostrado abaixo. Primeiro é criada uma tabela para conter os registros de auditoria gerados:
criar tabela ddl_log (operação varchar2(30),obj_owner varchar2(35),object_name varchar2(35),sql_text varchar2(200),attempt_by varchar2(35),attempt_dt timestamp); criar índice ddl_log_idx em ddl_log(obj_owner, operação);
A tabela é indexada em obj_owner e operação para acelerar a geração do relatório. Em seguida é criado um trigger como o usuário que possui as tabelas a serem monitoradas para registrar todas as instruções CREATE, ALTER e DROP que foram executadas:
criar ou substituir o gatilho ddl_triggerantes de criar ou alterar ou descartar esquemadeclare oper ddl_log.operation%type; sql_text ora_name_list_t; i pls_inteiro; begin i :=sql_txt(sql_text); se i =1 então insira em ddl_log selecione ora_sysevent, ora_dict_obj_owner, ora_dict_obj_name, sql_text(1), user, v_systimestamp de dual; elsif i =2 então insira em ddl_log selecione ora_sysevent, ora_dict_obj_owner, ora_dict_obj_name, sql_text(1)||sql_text(2), user, v_systimestamp de dual; elsif i>=3 então insira em ddl_log selecione ora_sysevent, ora_dict_obj_owner, ora_dict_obj_name, sql_text(1)||sql_text(2)||sql_text(3), user, v_systimestamp de dual; end if;end ddl_trigger;/
Como o número de 'partes' de 64 bytes do texto SQL pode ser bastante grande, o gatilho restringe a coluna SQL_TEXT às três primeiras 'partes', tornando o comprimento máximo da string de 192 caracteres. Como esperado para declarações maiores, o texto completo não será fornecido, mas deve capturar todas as declarações 'alter table' em sua totalidade. Observe que esse gatilho capturará não apenas as instruções ALTER TABLE, mas também qualquer instrução CREATE/ALTER/DROP enviada ao banco de dados. Isso significa que as instruções alter user, alter trigger, alter package, alter function, alter tablespace, alter system, create … e drop … também são registradas na tabela DDL_LOG. Por causa disso, a tabela pode crescer rapidamente e se tornar bastante grande, portanto, um plano para manter um histórico finito deve ser criado. Para a maioria dos sistemas, 90 dias devem ser suficientes para rastrear alterações de tabela no banco de dados. Os relatórios gerados a partir dos dados registrados podem ser mantidos por períodos mais longos (por exemplo, 12 meses) antes de serem removidos.
Um script de amostra para gerenciar os dados da tabela é fornecido abaixo; ele impõe uma janela de dados de 90 dias. Um diretório de log é criado:
mkdir -p /u01/app/oracle/ddl_chg/purge_logs
Um script SQL é escrito para limpar os registros antigos de DDL_LOG:
coluna sys_date new_value dt noprintcoluna name new_value db_nm noprintselect to_char(sysdate,'RRRRMMDD') sys_date de dual;selecione o nome de v$database;spool /u01/app/oracle/ddl_chg/purge_logs/ddl_log_purge_$db_nm._&dt.. logset echo on---- Registros programados para remoção--select * From ddl_log where try_dt
Isso, obviamente, não pode ser executado diretamente do cron (ou qualquer agendador semelhante), portanto, é necessário um script wrapper:
#!/bin/ksh## purge_ddl_log_90.sh## Shell script para limpar registros de auditoria antigos# da tabela DDL_LOG### Localize o banco de dados selecionado e defina o ambiente#set -A database `ps -ef | grep [p]seg | grep '' | awk -F"_" '{print $3}'`for i in ${database[@]}## Configura o ambiente para o banco de dados#do ORACLE_SID=$i export ORACLE_SID ORACLE_SID ORACLE_ASK=NÃO export ORAENV_ASK unset ORACLE_BASE export ORACLE_BASE PATH=$PATH: . /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## Inicie o SQL*Plus e execute o script# sqlplus /nolog <
O script de shell define o ambiente adequado e ORACLE_SID com base na saída do comando ps. O script precisará ser editado para fornecer o nome do banco de dados a ser pesquisado e o local ORACLE_HOME. Mais de um nome de banco de dados pode ser especificado usando | como separador:
'abd|def|ghi|jkl'
Isso fornece uma maneira de limpar a tabela DDL_LOG em cada banco de dados em que essa combinação de tabela/gatilho foi instalada. O nome do banco de dados é incluído no nome do arquivo de log para manter as trilhas de limpeza separadas para cada banco de dados. O período de tempo para manter os arquivos de log pode ser alterado para atender aos limites de armazenamento do sistema que está sendo monitorado.
Os relatórios de alterações podem ser gerados a partir dos dados encontrados na tabela DDL_LOG:
set linessize 140column sdate new_value sdt noprintselect to_Char(sysdate, 'RRRRMMDDHH24')sdate from dual;column modlen new_value mlen noprintselect 'a'||nvl(max(length(modification)),25) modlen From(select obj_owner owner , object_name tabname, substr(sql_text, instr(sql_text, 'modify')) modificação, try_dt mod_timefrom ddl_logwhere (instr(sql_text, 'alter table')> 0 ou instr(sql_text, 'ALTER TABLE')> 0));column objlen new_value olen noprintselect 'a'||nvl(max(length(owner||'.'||tabname)),60) objlen From(select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'modify ') ) modificação, try_dt mod_timefrom ddl_logwhere (instr(sql_text, 'alter table')> 0or instr(sql_text, 'ALTER TABLE')> 0));formato de modificação da coluna &mlencolumn mod_time format a29column tab_name format &olenselect owner||'.'|| tabname tab_name, modificação, mod_timefrom(select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'add')) modificação, tente_dt mod_timefrom ddl_logwhere instr(lower(sql_text), 'alter table')> 0unionselect obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'drop ')) modificação, try_dt mod_timefrom ddl_logwhere instr(lower(sql_text), 'alter table')> 0unionselect obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'modify ')) modificação, tente_dt mod_timefrom ddl_logwhere instr(lower(sql_text), 'alter table')> 0unionselect obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'ADD ')) modificação, tente_dt mod_timefrom ddl_logwhere instr(lower( sql_text), 'alter table')> 0unionselect obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'DROP')) modificação, try_dt mod_timefrom ddl_logwhere instr(lower(sql_text), 'alter table')> 0unionselect obj_owner owner , objeto _name tabname, substr(sql_text, instr(sql_text, 'MODIFY ')) modificação, try_dt mod_timefrom ddl_logwhere instr(lower(sql_text), 'alter table')> 0) dlwhere lower(dl.modification) não como '%table%' e mod_time>=trunc(systimestamp)ordem por 1, 3spool /u01/app/oracle/ddl_chg/log/tab_chg_rpt_&sdt._&1..lst/spool off
O nome do banco de dados é passado para o script para que seja incluído no nome do arquivo de relatório. O código relata apenas as alterações da tabela (daí a longa string de consultas UNION) e produz um relatório semelhante ao mostrado abaixo:
MODIFICAÇÃO DE TAB_NAME MOD_TIME---------------- ---------------------------- -- -----------------------------SCOTT.DDL_LOG modificar sql_text varchar2(200) 23-NOV-19 01.23.49.859971 PM
O script também define a formatação da coluna com base no comprimento máximo dos dados armazenados para possivelmente reduzir o comprimento da linha. Os dados de carimbo de data/hora foram usados para fornecer valores de data e hora visíveis para os registros de alteração gerados. Esses scripts foram testados, mas podem exigir algumas modificações com base na implementação do Linux/Unix pelo fornecedor do sistema operacional.
Para aqueles DBAs que não executam sistemas replicados, isso pode não ser de muita utilidade. Mas, para aqueles que replicam dados do Oracle para outros sistemas (como BigQuery, Snowflake e similares), saber quando ocorreram alterações na tabela pode facilitar o tratamento de falhas de replicação criadas por essas alterações. Quanto mais rápido o processo de replicação puder voltar aos trilhos, mais rápido os sistemas que dependem desses dados replicados poderão retornar à funcionalidade.
# # #
Veja artigos de David Fitzjarrell