Ajuda em procedure que cai performance durante execução
Ola a todos,
Eu desenvolvi uma procedure que, basicamente pega os dados de uma tabela carregada pelo loader e insere os dados em uma outra. A diferença é que um mesmo registro dessa tabela pode gerar 1 ou 2 inserts na outra tabela com um campo alterado.
Na tabela de origem tem aproximadamente 2 milhoes de registros, o que então pode gerar de 2 a 4 milhoes de registros na tabela de destino.
A procedure começa muito rápida, processando pelo menos 10 mil registro por minuto. O problema é que a medida que o tempo vai passando ela vai diminuindo a performance inexplicavelmente e leva 30 horas pra rodar!!!! Se mantivesse o tempo de processamento era pra acabar em menos de 4 horas.
Após 2 horas de processo essa velocidade de 10 mil/minuto já cai pra 5 mil/minuto e após algumas horas de processo para 500/minuto.
Já tentei dropar todos os indices, foreign keys, checks, etc da tabela de destino e nada funciona.
Como as tabelas são grandes não tem como inserir tudo de uma vez, senão eu destruo a tablespace temporária do banco. Então montei um loop que processa os dados de 5 em 5 mil:
A lógica é:
- Monta um cursor
- Select /+*FIRST ROWS*/ (pega os primeiros 5 mil registros)
- Insere na tabela destino
- Se necessário insere novamente na tabela de destino com um flag alterado
-> Fica nesse loop até processar os 2 milhoes de registros
Alguém tem alguma idéia????? QUALQUER uma? hehehe
Estou já bem atrasado no meu projeto e não consigo desagarrar disso.
Muito obrigado,
Luiz Fernando Heidenreich Reis
Eu desenvolvi uma procedure que, basicamente pega os dados de uma tabela carregada pelo loader e insere os dados em uma outra. A diferença é que um mesmo registro dessa tabela pode gerar 1 ou 2 inserts na outra tabela com um campo alterado.
Na tabela de origem tem aproximadamente 2 milhoes de registros, o que então pode gerar de 2 a 4 milhoes de registros na tabela de destino.
A procedure começa muito rápida, processando pelo menos 10 mil registro por minuto. O problema é que a medida que o tempo vai passando ela vai diminuindo a performance inexplicavelmente e leva 30 horas pra rodar!!!! Se mantivesse o tempo de processamento era pra acabar em menos de 4 horas.
Após 2 horas de processo essa velocidade de 10 mil/minuto já cai pra 5 mil/minuto e após algumas horas de processo para 500/minuto.
Já tentei dropar todos os indices, foreign keys, checks, etc da tabela de destino e nada funciona.
Como as tabelas são grandes não tem como inserir tudo de uma vez, senão eu destruo a tablespace temporária do banco. Então montei um loop que processa os dados de 5 em 5 mil:
A lógica é:
- Monta um cursor
- Select /+*FIRST ROWS*/ (pega os primeiros 5 mil registros)
- Insere na tabela destino
- Se necessário insere novamente na tabela de destino com um flag alterado
-> Fica nesse loop até processar os 2 milhoes de registros
Alguém tem alguma idéia????? QUALQUER uma? hehehe
Estou já bem atrasado no meu projeto e não consigo desagarrar disso.
Muito obrigado,
Luiz Fernando Heidenreich Reis
Luiz7s
Curtidas 0
Respostas
Rosterne
13/08/2006
Olhe o seguintes itens:
* Free buffer inspected --> Se esta aumentando. Essa medida mostra o número buffers que saltou sobre a extremidade de uma fila LRU a fim encontrar um buffer reusável. A diferença entre esta estatística e dirty buffers inspected é o número buffer que não poderiam ser usados porque eram ocupados e precisou ser reescritos pois teve um “rápido envelhecimento”.
Aconselho aumentar o buffer cache e/ou colocar tabelas que têm poucas chamadas em RECYCLE.
* Redo buffer allocation retries --> Tem que estar próximo de zero. Essa medida mostra a quantidade de vezes que o LGWR precisou de espaço no Redo Buffer mas não encontrou.
Sugiro:
- Aumentar o parâmetro log_buffer
- Setar o parâmetro log_checkpoint_timeout como zero
- Tirar processos paralelos de arquivamento de log.
Como você está dando carga grande, aconselho dá uma carga direta.
Isso fará com que reduza os logs do banco, não totalmente pq alguns DML podem gerar logs, como por exemplo os indices.
Ex:
INSERT /*+ APPEND */
INTO sales_fact
SELECT *
FROM customer_sales_fact_staging
ORDER BY time_wh_id;
COMMIT;
ou altere sua tabela pra NOLOGGING.
OBS: o parâmetro LOGGING / NOLOGGING especifica se as alterações nos dados serão registradas ou não para fins de recuperação. Embora o LOGGING faça com que as alterações demorem um pouco mais para serem executadas, deve ser usado quando estas alterações necessitem de recuperação. O NOLOGGING não pode ser especificado quando o parâmetro CACHE foi especificado.
Abraço.
* Free buffer inspected --> Se esta aumentando. Essa medida mostra o número buffers que saltou sobre a extremidade de uma fila LRU a fim encontrar um buffer reusável. A diferença entre esta estatística e dirty buffers inspected é o número buffer que não poderiam ser usados porque eram ocupados e precisou ser reescritos pois teve um “rápido envelhecimento”.
Aconselho aumentar o buffer cache e/ou colocar tabelas que têm poucas chamadas em RECYCLE.
* Redo buffer allocation retries --> Tem que estar próximo de zero. Essa medida mostra a quantidade de vezes que o LGWR precisou de espaço no Redo Buffer mas não encontrou.
Sugiro:
- Aumentar o parâmetro log_buffer
- Setar o parâmetro log_checkpoint_timeout como zero
- Tirar processos paralelos de arquivamento de log.
Como você está dando carga grande, aconselho dá uma carga direta.
Isso fará com que reduza os logs do banco, não totalmente pq alguns DML podem gerar logs, como por exemplo os indices.
Ex:
INSERT /*+ APPEND */
INTO sales_fact
SELECT *
FROM customer_sales_fact_staging
ORDER BY time_wh_id;
COMMIT;
ou altere sua tabela pra NOLOGGING.
OBS: o parâmetro LOGGING / NOLOGGING especifica se as alterações nos dados serão registradas ou não para fins de recuperação. Embora o LOGGING faça com que as alterações demorem um pouco mais para serem executadas, deve ser usado quando estas alterações necessitem de recuperação. O NOLOGGING não pode ser especificado quando o parâmetro CACHE foi especificado.
Abraço.
GOSTEI 0