Comment préextraire les ID de séquence Oracle dans un environnement distribué

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

  •  09-06-2019
  •  | 
  •  

Question

J'ai une application Java distribuée exécutée sur 5 serveurs d'applications.Les serveurs utilisent tous la même base de données Oracle 9i exécutée sur une 6ème machine.

L'application doit pré-extraire un lot de 100 identifiants à partir d'une séquence.C'est relativement facile à faire dans un environnement monothread et non distribué, vous pouvez simplement émettre ces requêtes :

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

La première sélection récupère le premier ID de séquence que l'application peut utiliser, la seconde sélection renvoie le dernier qui peut être utilisé.

Les choses deviennent bien plus intéressantes dans un environnement multithread.Vous ne pouvez pas être sûr qu'avant la seconde sélection, un autre thread n'augmentera pas à nouveau la séquence de 100.Ce problème peut être résolu en synchronisant l'accès côté Java - vous ne laissez qu'un seul thread commencer à récupérer les identifiants à la fois.

La situation devient vraiment difficile lorsque vous ne pouvez pas synchroniser car certaines parties de l'application ne s'exécutent pas sur la même JVM, ni même sur la même machine physique.J'ai trouvé des références sur des forums selon lesquelles d'autres ont également du mal à résoudre ce problème, mais aucune des réponses ne fonctionne vraiment, sans parler d'être raisonnable.

La communauté peut-elle apporter une solution à ce problème ?

Quelques informations supplémentaires :

  • Je ne peux pas vraiment jouer avec les niveaux d'isolement des transactions.J'utilise JPA et le changement affecterait l'ensemble de l'application, pas seulement les requêtes de prélecture, ce qui n'est pas acceptable pour moi.
  • Sur PostgreSQL, je pourrais faire ce qui suit :

    sélectionnez setval('seq', nextval('seq') + n - 1 )

  • La solution de Matthew fonctionne lorsque vous pouvez utiliser une valeur d'incrément fixe (ce qui est parfaitement acceptable dans mon cas).Cependant existe-t-il une solution lorsque l'on ne souhaite pas fixer la taille de l'incrément, mais que l'on souhaite l'ajuster dynamiquement ?

Était-ce utile?

La solution

Pourquoi ne pas simplement avoir la séquence incrémentée de 100 tout le temps ?chaque "nextval" vous donne 100 numéros de séquence avec lesquels travailler

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> 

Une note sur votre exemple..Attention au DDL..Cela produira un commit implicite

Exemple de commit produit par 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> 

Autres conseils

Pourquoi avez-vous besoin de récupérer les ID de séquence en premier lieu ?Dans la plupart des cas, vous insérez dans une table et renvoyez l'ID.

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

Il semble que vous essayiez de pré-optimiser le traitement.

Si vous avez VRAIMENT besoin de pré-récupérer les identifiants, appelez simplement la séquence 100 fois.Tout l’intérêt d’une séquence est qu’elle gère la numérotation.Vous n'êtes pas censé supposer que vous pouvez obtenir 100 numéros consécutifs.

Matthew a la bonne approche ici.À mon avis, il est très inhabituel qu'une application réinitialise la valeur actuelle d'une séquence après chaque utilisation.Il est beaucoup plus conventionnel de définir la taille de l'incrément selon vos besoins à l'avance.

De plus, cette méthode est beaucoup plus performante.La sélection de nextval à partir d'une séquence est une opération hautement optimisée dans Oracle, alors qu'exécuter ddl pour modifier la séquence est beaucoup plus coûteux.

Je suppose que cela ne répond pas vraiment au dernier point de votre question modifiée...

Car lorsque vous ne voulez pas d'incrément de taille fixe, les séquences ne sont pas vraiment ce que vous recherchez, tout ce qu'elles garantissent réellement, c'est que vous obtiendrez un numéro unique toujours plus grand que le dernier que vous avez obtenu.Il est toujours possible que vous vous retrouviez avec des écarts et que vous ne puissiez pas vraiment ajuster le montant de l'incrément à la volée, de manière sûre ou efficace.

Je ne peux pas vraiment penser à un cas où j'ai dû faire ce genre de chose, mais le moyen le plus simple est probablement de simplement stocker le numéro "actuel" quelque part et de le mettre à jour selon vos besoins.

Quelque chose comme ça.

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> 
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top