HELP PERFORMANCE - URGENTE MESMO!!!!

Dúvidas, dicas e truques de PL/SQL. Aqui também vão assuntos relacionados a pacotes, triggers, funções, Java-Stored Procedures, etc
Responder
odelar
Rank: Estagiário Sênior
Rank: Estagiário Sênior
Mensagens: 14
Registrado em: Ter, 03 Mar 2009 3:12 pm
Localização: BARUERI - SP

Boa tarde, pessoal. Estou com uma aplicação que esta com quase 3 dias pra concluir em homologação e preciso melhorar a performance pra executar em produção... URGENTE!!!

Estou executando uma atualização, onde busco
informações em uma tabela de transações com aproximandamente uns 10
milhões de linhas (ultima compra/credito) e faço um update em outra
tabela com 1,5 de linhas.

Criei uma tabela onde tenho o numero dos cartões e uma sequencia,
assim consigo executar o mesmo processo simultaneamente no banco. Startei 16 sessões diferentes no servidor, cada uma executando um grupo de sequências, processando paralelamente a mesma rotina, pra cartões diferentes.

O grande problema é que esta executando fazem mais de 70 horas e
ainda não acabou! Preciso urgente de uma ajuda!!!!

Alguém pode me ajudar?


Segue dados e processo abaixo:

Banco: Oracle 10g


Selecionar tudo

CREATE OR REPLACE PROCEDURE Atualizar_Snccr( p_Seq1 GRUPO_SNCCR.SEQ%TYPE
                                           , p_Seq2 GRUPO_SNCCR.SEQ%TYPE ) IS
--
  CURSOR c_Comp(pc_CrtNum SNCRT.CRTNUM%TYPE) IS 
    SELECT /*+ INDEX(SNTRN,ISNTRN19) */ SNTRN.TRNDAT, SNTRN.TRNVAL 
      FROM SNTRN 
     WHERE SNTRN.TRNPRECRT  = pc_CrtNum 
       AND SNTRN.TRNPRCCOD IN (240000,241000,770000,778000) 
       AND SNTRN.TRNDAT     = (SELECT /*+ INDEX(SNTRN,ISNTRN19) */ MAX(SNTRN.TRNDAT) 
                                 FROM SNTRN 
                                WHERE SNTRN.TRNPRECRT  = pc_CrtNum
                                  AND SNTRN.TRNPRCCOD IN (240000,241000,770000,778000));
  --
  CURSOR c_Cred(pc_CrtNum SNCRT.CRTNUM%TYPE) IS 
    SELECT /*+ INDEX(SNTRN,ISNTRN20) */ SNTRN.TRNDAT, SNTRN.TRNVAL 
      FROM SNTRN 
     WHERE SNTRN.TRNSUBCRT  = pc_CrtNum
       AND SNTRN.TRNPRCCOD IN (620000,621000,625000)
       AND SNTRN.TRNDAT     = (SELECT /*+ INDEX(SNTRN,ISNTRN20) */ MAX(SNTRN.TRNDAT) 
                                 FROM SNTRN 
                                WHERE SNTRN.TRNSUBCRT  = pc_CrtNum
                                  AND SNTRN.TRNPRCCOD IN (620000,621000,625000)); 
  --
  r_TrnDat_Comp SNTRN.TRNDAT%TYPE;
  r_TrnVal_Comp SNTRN.TRNVAL%TYPE;
  r_TrnDat_Cred SNTRN.TRNDAT%TYPE;
  r_TrnVal_Cred SNTRN.TRNVAL%TYPE;
  r_CrtNum      SNCRT.CRTNUM%TYPE;
  --
  v_Achou         NUMBER(1);
  v_qtd_registros NUMBER(10);
--
BEGIN
--
  -- inicializa variaveis
  r_TrnDat_Comp   := TO_DATE('01/01/0001','DD/MM/YYYY');
  r_TrnVal_Comp   := 0;
  r_TrnDat_Cred   := TO_DATE('01/01/0001','DD/MM/YYYY');
  r_TrnVal_Cred   := 0;
  v_qtd_registros := 0;
  v_achou         := 0;
  --
  FOR r_Crt IN (SELECT SEQ
                     , CRTNUM
                  FROM GRUPO_SNCCR
                 WHERE GRUPO_SNCCR.SEQ  BETWEEN p_Seq1 AND p_Seq2
                   AND GRUPO_SNCCR.PROCESSADO = 0                ) LOOP
  --
    -- busca dados da ultima compra e atualiza
    BEGIN 
    --
      SELECT 1 INTO v_Achou
        FROM SNCCR
       WHERE SNCCR.CRTNUM         = r_Crt.crtnum
         AND SNCCR.APLCOD         = '02001'
         AND SNCCR.CCRCMPRULTDAT IS NULL
         AND SNCCR.CCRCMPRULTVLR IS NULL 
         AND SNCCR.CCRCRDULTDAT  IS NULL 
         AND SNCCR.CCRCRDULTVLR  IS NULL;
      --
      IF v_Achou = 1 THEN
      --
        -- busca dados da ultima compra e atualiza
        OPEN c_Comp(r_Crt.crtnum);
        FETCH c_Comp INTO r_TrnDat_Comp, r_TrnVal_Comp;
        CLOSE c_Comp;
        --
        -- busca dados do ultimo credito e atualiza
        OPEN c_Cred(r_Crt.crtnum);
        FETCH c_Cred INTO r_TrnDat_Cred, r_TrnVal_Cred;
        CLOSE c_Cred;
        --
        UPDATE SNCCR
           SET SNCCR.CCRCMPRULTDAT = r_TrnDat_Comp
             , SNCCR.CCRCMPRULTVLR = r_TrnVal_Comp     
             , SNCCR.CCRCRDULTDAT  = r_TrnDat_Cred
             , SNCCR.CCRCRDULTVLR  = r_TrnVal_Cred
         WHERE SNCCR.CRTNUM        = r_Crt.crtnum
           AND SNCCR.APLCOD        = '02001';
        --  
        v_qtd_registros := v_qtd_registros + 1;
        --
        -- Zera variaveis de data e valor, para o proximo cartao
        r_TrnDat_Comp := TO_DATE('01/01/0001','DD/MM/YYYY');
        r_TrnVal_Comp := 0;
        r_TrnDat_Cred := TO_DATE('01/01/0001','DD/MM/YYYY');
        r_TrnVal_Cred := 0;
        --
        UPDATE GRUPO_SNCCR
           SET GRUPO_SNCCR.PROCESSADO = 1
             , GRUPO_SNCCR.GCRATUDAT  = SYSDATE
         WHERE GRUPO_SNCCR.SEQ        = r_Crt.Seq
           AND GRUPO_SNCCR.CRTNUM     = r_Crt.CrtNum;
        --
        IF v_qtd_registros = 1000 THEN 
          COMMIT;
          v_qtd_registros := 0;
        END IF;
        --
        v_Achou := 0;
      -- 
      END IF;
    --
    END;  
  --                   
  END LOOP; 
  --
  IF v_qtd_registros <> 0 THEN 
    COMMIT;
  END IF; 
--
END;
Avatar do usuário
dr_gori
Moderador
Moderador
Mensagens: 5024
Registrado em: Seg, 03 Mai 2004 3:08 pm
Localização: Portland, OR USA
Contato:
Thomas F. G

Você já respondeu a dúvida de alguém hoje?
https://glufke.net/oracle/search.php?search_id=unanswered

Acho que primeiramente você tinha que descobir O QUE está demorando.
Coloca uns DBMS_OUTPUT, ou alguma outra forma de logar. Roda uns TRACE pra saber onde está demorando.

Roda sua procedure pra algumas poucas linhas...
Depois de saber ONDE está o problema, certamente a coisa fica mais fácil.
odelar
Rank: Estagiário Sênior
Rank: Estagiário Sênior
Mensagens: 14
Registrado em: Ter, 03 Mar 2009 3:12 pm
Localização: BARUERI - SP

Thomas, rodamos o trace e o problema é nas querys da tabela SNTRN.

Minha dúvida é se existe alguma forma de melhorar o a rotina. seria viavel a utilização de bulk colletion?
Avatar do usuário
dr_gori
Moderador
Moderador
Mensagens: 5024
Registrado em: Seg, 03 Mai 2004 3:08 pm
Localização: Portland, OR USA
Contato:
Thomas F. G

Você já respondeu a dúvida de alguém hoje?
https://glufke.net/oracle/search.php?search_id=unanswered

Você disse que está lento na tabela SNTRN:

Já rodou um explain pra ver se os índices do seu HINT tão mesmo sendo usados?

Selecionar tudo

SELECT /*+ INDEX(SNTRN,ISNTRN19) */ SNTRN.TRNDAT, SNTRN.TRNVAL
      FROM SNTRN
     WHERE SNTRN.TRNPRECRT  = pc_CrtNum
       AND SNTRN.TRNPRCCOD IN (240000,241000,770000,778000)
       AND SNTRN.TRNDAT     = (SELECT /*+ INDEX(SNTRN,ISNTRN19) */ MAX(SNTRN.TRNDAT)
                                 FROM SNTRN
                                WHERE SNTRN.TRNPRECRT  = pc_CrtNum
                                  AND SNTRN.TRNPRCCOD IN (240000,241000,770000,778000)); 
Pois normalmente não colocamos vírgula depois do nome da tabela no HINT. Eu faria assim: (caso SNTRN19 seja o indice de SNTRN)

Selecionar tudo

/*+ INDEX(SNTRN ISNTRN19) */
odelar
Rank: Estagiário Sênior
Rank: Estagiário Sênior
Mensagens: 14
Registrado em: Ter, 03 Mar 2009 3:12 pm
Localização: BARUERI - SP

Sim executei o plano... e tanto faz usar o hint com ou sem a virgula...

Não conheço muito bem como usar o bulk colletion, como poderia aplicar nesta rotina?
Avatar do usuário
dr_gori
Moderador
Moderador
Mensagens: 5024
Registrado em: Seg, 03 Mai 2004 3:08 pm
Localização: Portland, OR USA
Contato:
Thomas F. G

Você já respondeu a dúvida de alguém hoje?
https://glufke.net/oracle/search.php?search_id=unanswered

Bem, não sei se ia adiantar muito colocar BULK aí, pois ele apenas busca várias linhas "em massa" e guarda na memória.
Como o problema não é buscar as linhas, e sim esses 2 cursores lentos, acho que não vai dar um ganho grande, mas não custa tentar.

Aqui tem um ótimo texto que saiu na ORACLE magazine:
http://www.oracle.com/technology/oramag ... plsql.html

Também tem aqui no forum alguns exemplos. Faça uma pesquisa por BULK aqui no forum que você acha vários.
Avatar do usuário
fsitja
Rank: OraSauro
Rank: OraSauro
Mensagens: 611
Registrado em: Seg, 19 Jan 2009 4:29 pm
Localização: Gaúcho no Rio de Janeiro - RJ
"The scars exist to remind us that the past was real"
Campanha: Como fazer uma pergunta e obter uma resposta.
http://tkyte.blogspot.com/2005/06/how-t ... tions.html

OCA & OCP Developer — OCE SQL Expert — OCS Data Warehousing Specialist

A vantagem de usar bulk collect em conjunto com update forall é exatamente evitar o chaveamento de contexto entre o PL/SQL engine e a query SQL. Para cada select individual que você manda, o servidor sofre com um overhead. A princípio ele é pequeno, se você executa poucas vezes, mas em loops muito longos e problema fica evidente e a queda de desempenho é gritante. Tanto é verdade que a Oracle recomenda que não se use CURSOR FOR LOOP para qualquer código novo que se faça, pois o bulk collect é superior sempre, exceto quando você precisa de um select into. Apenas lembre-se que o bulk collect carrega em memória tudo, e que a cláusula LIMIT ajuda a reduzir o consumo de recursos no servidor.

O outro problema é inerente ao modelo de dados da solução. Seus índices possuem as colunas TRNPRECRT, TRNPRCCOD e TRNDAT nas suas primeiras colunas? Porque se não for, o oracle pode estar fazendo um "index skip scan", o que reduz o desempenho do seu índice consideravelmente.

Por outro lado agora, repare em:

Selecionar tudo

SNTRN.TRNDAT     = (SELECT /*+ INDEX(SNTRN,ISNTRN19) */ MAX(SNTRN.TRNDAT)
Esse select max provavelmente é quem está acabando com você. Repare que ele causa um self join no seu tabelão. Você vai ter que evitar isso. Fica difícil dizer como resolver sem conhecer seus requisitos, mas talvez seja preciso alterar seu modelo de dados, criando dados redundantes ou pré-calculados. Obviamente isso onera as suas transações para você garantir a consistência através de restrições de integridade e triggers.
erthal
Rank: Analista Sênior
Rank: Analista Sênior
Mensagens: 130
Registrado em: Seg, 22 Nov 2004 1:45 pm
Localização: Niterói - RJ
Gustavo Erthal Jr. | TRISCAL
...................................................
Rio de Janeiro | (21) 2507-2010
São Paulo | (11) 3167-0526
www.triscal.com.br

Odelar,

Você disse que está utilizando o Oracle10g que possui um otimizador melhor que nas versões anteriores.

Gostaria de saber se suas tabelas e índices foram analisados recentemente. Se foram, faça um teste, rode um explain plan sem os hints e envie ambos os resultados.

O bulk é uma solução mas como já foi dito ele armazena em memória um bloco de informações.

Procure descobrir onde está sua contenção, verifique se há problemas de contenção nos Redo Log Files, medindo o tempo entre cada log switch.

Me forneça um e-mail para eu te enviar alguns scripts interessantes para verificar os porquês dessas demoras.

Abraços,
RodrigoValentim
Moderador
Moderador
Mensagens: 367
Registrado em: Ter, 25 Mar 2008 3:41 pm
Localização: Salvador - BA
Rodrigo Valentim
Analista de Sistemas
Oracle Developer

Campanha: Faça uma pesquisa antes de perguntar!!!

Primeiro passo a fazer é mandar pra gente um trace do que está sendo feito, ou até mesmo um explain... você já tentou tirar o /*+ INDEX(SNTRN,ISNTRN19) */ do seu código? falo isso porque nas versões mais novas forcar o uso nem sempre é legal... deixa ele gerenciar isso... principalmente por que você colocou 16 sessoes pra trabalhar usando a mesma tabela / indice.

Existe algum campo data pra você colocar como sendo filtro? por exemplo, já que se trata de cartão, pega a data da adesão do cliente para este cartão e coloca como sendo base de filtro para as outras querys, dessa forma, a coisa já vai ficar bem mais leve.

Posta aí os restulados.

Abraço!
Responder
  • Informação
  • Quem está online

    Usuários navegando neste fórum: Nenhum usuário registrado e 19 visitantes