Cómo capturar previamente los ID de secuencia de Oracle en un entorno distribuido

StackOverflow https://stackoverflow.com/questions/43808

  •  09-06-2019
  •  | 
  •  

Pregunta

Tengo una aplicación Java distribuida que se ejecuta en 5 servidores de aplicaciones.Todos los servidores utilizan la misma base de datos Oracle 9i que se ejecuta en una sexta máquina.

La aplicación debe obtener previamente un lote de 100 ID de una secuencia.Es relativamente fácil de hacer en un entorno no distribuido de un solo subproceso; simplemente puede emitir estas consultas:

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

La primera selección recupera el primer ID de secuencia que la aplicación puede usar, la segunda selección devuelve el último que se puede usar.

Las cosas se vuelven mucho más interesantes en un entorno multiproceso.No puede estar seguro de que antes de la segunda selección de otro hilo no aumente la secuencia en 100 nuevamente.Este problema se puede resolver sincronizando el acceso en el lado de Java: solo permite que un subproceso comience a buscar los ID a la vez.

La situación se vuelve realmente difícil cuando no se puede sincronizar porque partes de la aplicación no se ejecutan en la misma JVM, ni siquiera en la misma máquina física.Encontré algunas referencias en foros de que otros también tienen problemas para resolver este problema, pero ninguna de las respuestas realmente funciona, sin mencionar que es razonable.

¿Puede la comunidad proporcionar una solución a este problema?

Más información:

  • Realmente no puedo jugar con los niveles de aislamiento de transacciones.Utilizo JPA y el cambio afectaría a toda la aplicación, no solo a las consultas de captación previa, y eso no es aceptable para mí.
  • En PostgreSQL podría hacer lo siguiente:

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

  • La solución de Matthew funciona cuando puedes usar un valor de incremento fijo (lo cual es perfectamente aceptable en mi caso).Sin embargo, ¿existe una solución cuando no desea fijar el tamaño del incremento, pero desea ajustarlo dinámicamente?

¿Fue útil?

Solución

¿Por qué no hacer que la secuencia aumente en 100 todo el tiempo?cada "nextval" le proporciona 100 números de secuencia con los que trabajar

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> 

Una nota sobre tu ejemplo.Cuidado con DDL..Producirá un compromiso implícito.

Ejemplo de compromiso producido 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> 

Otros consejos

¿Por qué es necesario recuperar los ID de secuencia en primer lugar?En la mayoría de los casos, lo insertaría en una tabla y devolvería el ID.

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

Parece que está intentando optimizar previamente el procesamiento.

Si REALMENTE necesita obtener previamente las ID, simplemente llame a la secuencia 100 veces.El objetivo de una secuencia es que gestiona la numeración.Se supone que no debes asumir que puedes obtener 100 números consecutivos.

Mateo tiene el enfoque correcto aquí.En mi opinión, es muy inusual que una aplicación restablezca el valor actual de una secuencia después de cada uso.Es mucho más convencional establecer el tamaño del incremento en lo que necesite por adelantado.

Además, de esta manera es mucho más eficaz.Seleccionar nextval de una secuencia es una operación altamente optimizada en Oracle, mientras que ejecutar ddl para alterar la secuencia es mucho más costoso.

Supongo que eso realmente no responde al último punto de tu pregunta editada...

Para cuando no desea un incremento de tamaño fijo, las secuencias no son realmente lo que busca, lo único que realmente garantizan es que obtendrá un número único siempre mayor que el último que obtuvo.Siempre existe la posibilidad de que termines con brechas y realmente no puedas ajustar la cantidad de incremento sobre la marcha de manera segura o efectiva.

Realmente no puedo pensar en ningún caso en el que haya tenido que hacer este tipo de cosas, pero probablemente la forma más fácil sea simplemente almacenar el número "actual" en algún lugar y actualizarlo cuando lo necesite.

Algo como esto.

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top