Pergunta

Tenho um aplicativo Java distribuído em execução em 5 servidores de aplicativos.Todos os servidores usam o mesmo banco de dados Oracle 9i em execução em uma sexta máquina.

O aplicativo precisa buscar previamente um lote de 100 IDs de uma sequência.É relativamente fácil de fazer em um ambiente de thread único e não distribuído. Basta emitir estas consultas:

select seq.nextval from dual;
alter sequence seq increment by 100;
select seq.nextval from dual;

O primeiro select busca o primeiro ID de sequência que a aplicação pode utilizar, o segundo select retorna o último que pode ser utilizado.

As coisas ficam muito mais interessantes em um ambiente multithread.Você não pode ter certeza de que antes da segunda seleção outro thread não aumentará a sequência em 100 novamente.Esse problema pode ser resolvido sincronizando o acesso no lado Java - você permite apenas que um thread comece a buscar os IDs por vez.

A situação fica muito difícil quando você não consegue sincronizar porque partes do aplicativo não rodam na mesma JVM, nem mesmo na mesma máquina física.Encontrei algumas referências em fóruns de que outras pessoas também têm problemas para resolver esse problema, mas nenhuma das respostas está realmente funcionando, sem falar que é razoável.

A comunidade pode fornecer uma solução para este problema?

Mais algumas informações:

  • Eu realmente não posso brincar com os níveis de isolamento das transações.Eu uso JPA e a mudança afetaria todo o aplicativo, não apenas as consultas de pré-busca e isso não é aceitável para mim.
  • No PostgreSQL eu poderia fazer o seguinte:

    selecione setval('seq', nextval('seq') + n - 1 )

  • A solução de Matthew funciona quando você pode usar um valor de incremento fixo (o que é perfeitamente aceitável no meu caso).No entanto, existe uma solução quando você não deseja corrigir o tamanho do incremento, mas deseja ajustá-lo dinamicamente?

Foi útil?

Solução

Por que não simplesmente aumentar a sequência em 100 o tempo todo?cada "nextval" fornece 100 números de sequência para trabalhar

SQL> create sequence so_test start with 100 increment by 100 nocache;

Sequence created.

SQL> select so_test.nextval - 99 as first_seq, so_test.currval as last_seq from dual;

 FIRST_SEQ   LAST_SEQ
---------- ----------
         1        100

SQL> /

 FIRST_SEQ   LAST_SEQ
---------- ----------
       101        200

SQL> /

 FIRST_SEQ   LAST_SEQ
---------- ----------
       201        300

SQL> 

Uma observação sobre o seu exemplo.Cuidado com DDL..Isso produzirá um commit implícito

Exemplo de commit produzido por DDL

SQL> select * from xx;

no rows selected

SQL> insert into xx values ('x');

1 row created.

SQL> alter sequence so_test increment by 100;

Sequence altered.

SQL> rollback;

Rollback complete.

SQL> select * from xx;

Y
-----
x

SQL> 

Outras dicas

Por que você precisa buscar os IDs de sequência em primeiro lugar?Na maioria dos casos você inseriria em uma tabela e retornaria o ID.

insert into t (my_pk, my_data) values (mysequence.nextval, :the_data)
returning my_pk into :the_pk;

Parece que você está tentando pré-otimizar o processamento.

Se você REALMENTE precisar pré-buscar os IDs, basta chamar a sequência 100 vezes.O objetivo de uma sequência é que ela gerencia a numeração.Você não deve presumir que pode obter 100 números consecutivos.

Mateus tem a abordagem correta aqui.Na minha opinião, é muito incomum que um aplicativo redefina o valor atual de uma sequência após cada uso.Muito mais convencional definir o tamanho do incremento para o que você precisar antecipadamente.

Além disso, essa forma tem muito mais desempenho.Selecionar nextval de uma sequência é uma operação altamente otimizada no Oracle, enquanto executar ddl para alterar a sequência é muito mais caro.

Acho que isso realmente não responde ao último ponto da sua pergunta editada ...

Pois quando você não deseja um incremento de tamanho fixo, as sequências não são realmente o que você procura, tudo o que elas realmente garantem é que você obterá um número único sempre maior que o último obtido.Sempre existe a possibilidade de você acabar com lacunas e não conseguir ajustar o valor do incremento rapidamente com segurança ou eficácia.

Na verdade, não consigo pensar em nenhum caso em que precisei fazer esse tipo de coisa, mas provavelmente a maneira mais fácil é armazenar o número "atual" em algum lugar e atualizá-lo conforme necessário.

Algo assim.

drop table t_so_test;

create table t_so_test (curr_num number(10));

insert into t_so_test values (1);
create or replace procedure p_get_next_seq (inc IN NUMBER, v_next_seq OUT NUMBER) As
BEGIN
  update t_so_test set curr_num = curr_num + inc RETURNING curr_num into v_next_seq;
END;
/


SQL> var p number;
SQL> execute p_get_next_seq(100,:p);

PL/SQL procedure successfully completed.

SQL> print p;

         P
----------
       101

SQL> execute p_get_next_seq(10,:p);     

PL/SQL procedure successfully completed.

SQL> print p;

         P
----------
       111

SQL> execute p_get_next_seq(1000,:p);

PL/SQL procedure successfully completed.

SQL> print p;

         P
----------
      1111

SQL> 
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top