2017-08-04 3 views
1

Il s'agit d'une vieille question, mais pour être sûr que je demande à nouveau. En fait, j'ai créé une séquence comme dans Oracle avec une table et que vous voulez utiliser avec plusieurs threads et plusieurs JVM tout le processus sera en parallèle. La procédure suivante stockée par séquence veut simplement demander si cela fonctionnera avec plusieurs JVM et fournira toujours un numéro unique aux threads dans tous les jvm ou y a-t-il une faible chance qu'elle renvoie le même numéro de séquence deux plus d'un appel?Création d'un niveau JVM et d'une séquence Safe Thread dans la base de données

create table sequenceTable (id int) 
insert into sequenceTable values (0) 

create procedure mySequence 
AS 
BEGIN 
    declare @seqNum int 
    declare @rowCount int 

    select @rowCount = 0 
    while(@rowCount = 0) 
    begin 
     select @seqNum = id from sequenceTable 
     update sequenceTable set id = id + 1 where id = @seqNum 
     select @rowCount = @@rowcount 
     print 'numbers of rows update %1!', @rowCount 
    end 
    SELECT @seqNum 
END 
+0

Je ne pense pas qu'il y ait de synchronisation dans votre procédure - cela signifie que vos premières instructions 'select' et' update' ont une course - il est possible de sélectionner simultanément un autre processus pour toucher votre table entre select et update d'un autre processus, vous donnant ainsi deux des mêmes valeurs. (Votre question n'est pas liée à Java, d'ailleurs). –

+0

D'accord avec @ M.Prokhorov - ce n'est pas strictement une question Java. Même ainsi, pourquoi ne pas simplement utiliser une séquence réelle? Les applications Java multithread les utilisent depuis des années –

+0

Parce que db ne fournit pas de séquence sybase et que cette procédure stockée sera appelée par plusieurs JVM et threads. Aussi la mise à jour ne fonctionnera que quand la condition rencontrera la même version que je le ferais sinon elle ajoutera une nouvelle valeur, je l'ai testée avec 500 appels du test d'unité Java et chaque fois cela donne des résultats différents. Je demande à confirmer peut-être quelques choses hits. – user460293

Répondre

1

Si vous choisissez de maintenir votre conception actuelle de mise à jour de la colonne sequenceTable.id chaque fois que vous souhaitez générer un nouveau numéro de séquence, vous devez vous assurer:

  • le processus « courant » obtient un verrou exclusif sur la ligne contenant le numéro de séquence désirée
  • le processus « courant » met alors à jour la ligne désirée et extrait la valeur nouvellement mis à jour
  • le processus « courant » libère le verrou exclusif

Bien que ce qui précède peut être mis en œuvre au moyen d'un begin tran + update + select + commit tran, il est en fait un peu plus facile avec une seule instruction update, par exemple:

create procedure mySequence 
AS 
begin 
    declare @seqNum int 

    update sequenceTable 
    set @seqNum = id + 1, 
      id  = id + 1 

    select @seqNum 
end 

La déclaration update est sa propre transaction si la mise à jour La colonne id et l'affectation @seqNum = id + 1 sont exécutées sous un verrou exclusif dans la transaction update. Gardez à l'esprit que le verrou exclusif empêchera les autres processus d'obtenir une nouvelle valeur d'ID; Le résultat net est que la génération de nouvelles valeurs d'identifiant sera monothread/séquentielle

Bien que cela soit 'bon' dans la perspective de s'assurer que tous les processus obtiennent une valeur unique, cela signifie que cette instruction particulière devient un goulot d'étranglement si vous avez plusieurs processus qui touchent le update simultanément.

Dans une telle situation (volume élevé de update s), vous pouvez alléger certains conflits en appelant le proc stocké moins souvent; ceci pourrait être accompli en demandant aux processus appelant une plage de nouvelles valeurs id (par exemple, passer @increment comme paramètre d'entrée à la proc, alors au lieu de id + 1 vous utilisez id + @increment), le processus appelant sachant alors qu'il peut utiliser des numéros de séquence (@[email protected]+1) à @seqNum.


Évidemment (?) tout processus qui utilise un proc stocké pour générer des valeurs 'next id' ne fonctionne que si * ALL * processus a) appelle toujours le proc pour une nouvelle valeur d'identifiant et b) * TOUS * les processus utilisent uniquement la valeur d'identifiant retournée par le proc (par exemple, ils ne génèrent pas leurs propres valeurs d'id).

S'il existe une possibilité d'applications ne suivant pas ce processus (appelez proc pour obtenir une nouvelle valeur d'ID), vous pouvez envisager de pousser la création des valeurs d'ID uniques vers la table où ces valeurs sont insérées; en d'autres termes, modifiez la colonne id de la table cible pour inclure l'attribut identity; ceci élimine le besoin pour les applications d'appeler le proc stocké (pour générer un nouvel identifiant) et assure (toujours) qu'un identifiant unique est généré pour chaque insert.

+0

Nice! Merci... – user460293

1

Vous pouvez émuler des séquences dans ASE. Utilisez la fonction reserve_identity pour obtenir le type d'activité requis:

create table sequenceTable (id bigint identity) 
go 

create procedure mySequence AS 
begin 
    select reserve_identity('sequenceTable', 1) 
end 
go 

Cette solution est non bloquant et ne génère une activité de journal des transactions minimes.

+0

Cela garantira-t-il qu'il ne fournira pas de valeurs dupliquées même avec plusieurs JVM? – user460293

+0

Vous utilisez le mécanisme IDENTITY standard de la base de données. La base de données devrait vous garantir cela. Mais rappelez-vous - chaque identité a besoin de sa propre table. Si vous voulez utiliser une seconde identité, vous devez créer une table suivante. –

+0

@ user460293; remarquez qu'Adam a modifié votre colonne 'sequenceTable.id' de' int' en 'bigint identity'; alors que l'utilisation de 'int' vs' bigint' est à vous de décider, l'attribut 'identity' doit être fourni pour que la fonction' reserve_identity (, ) fonctionne correctement – markp