9
votes

Atomic comparer et échanger dans une base de données

Je travaille sur une solution de file d'attente de travail. Je souhaite interroger une ligne donnée dans la base de données, où une colonne d'état a une valeur spécifique, modifie cette valeur et renvoyer la ligne, et je tiens à le faire atomiquement, de sorte qu'aucune autre requête ne le ne le ne le ne le verra:


begin transaction
select * from table where pk = x and status = y
update table set status = z where pk = x
commit transaction
--(the row would be returned)


2 commentaires

Votre solution de file d'attente doit-elle fonctionner à la fois dans Oracle et SQL Server? Si seulement Oracle, vous devriez probablement regarder dans l'oracle avancé en file d'attente.


@Stephen Nope - il doit travailler avec les deux. Nous vendons ce logiciel et nous prenons en charge les deux côtés. Nous constatons que notre meilleure stratégie consiste à ne pas compter sur des fonctionnalités spécifiques au produit et d'essayer de ne utiliser que la syntaxe ANSI SQL, dans la mesure du possible (bien que nous puissions parvenir à une casse-casson, notre syntaxe).


4 Réponses :


2
votes

J'ai quelques applications qui suivent un modèle similaire. Il y a une table comme la vôtre qui représente une file d'attente de travail. La table a deux colonnes supplémentaires: thread_id et thread_date. Lorsque l'application demande au travail, la file d'attente, il soumet un identifiant de thread. Ensuite, une seule instruction de mise à jour met à jour toutes les lignes applicables avec la colonne ID de thread avec l'ID soumis et la colonne Date de filetage avec l'heure actuelle. Après cette mise à jour, il sélectionne toutes les lignes avec cet identifiant de thread. De cette façon, vous n'avez pas besoin de déclarer une transaction explicite. Le "verrouillage" se produit dans la mise à jour initiale.

La colonne thread_date est utilisée pour vous assurer que vous ne vous retrouvez pas avec des éléments de travail orphelins. Que se passe-t-il si les articles sont tirés de la file d'attente, puis votre application se bloque? Vous devez avoir la capacité d'essayer à nouveau ces objets de travail. Vous pouvez donc saisir tous les éléments de la file d'attente qui n'ont pas été marqués terminés mais ont été attribués à un thread avec une date de filetage dans le passé lointain. C'est à vous de définir "distant".


0 commentaires

2
votes

En règle générale, pour faire une opération comme cet atomique, vous devez vous assurer de définir une serrure exclusive (ou mise à jour) lorsque vous effectuez la sélection de SELECT afin qu'aucune autre transaction ne puisse lire la ligne avant votre mise à jour. < p> La syntaxe typique pour cela est quelque chose comme: xxx

mais vous devez le chercher à être sûr.


1 commentaires

Notez que cela n'arrête pas une autre session en lisant cette ligne, à moins que cette autre session n'utilise également la clause "pour la mise à jour".



1
votes

Essayez ceci. La validation est dans l'instruction de mise à jour.

Code H2>
ColID Status
----- -------------
    1 Updated Value

@initialValue @newValue
------------- -------------
Initial Value Updated Value


0 commentaires

9
votes

est pk la clé primaire? Ensuite, il s'agit d'un problème non-problème, si vous connaissez déjà la clé principale, il n'y a pas de sport. Si pk est la clé primaire, cela supplique la question évidente comment connaissez-vous le PK de l'élément à déquender ...

Le problème est si vous < EM> Ne pas connaissez la clé primaire et souhaitez détresuer le suivant "disponible" (c.-à-d. Statut = y) et marquez-le comme déséquilibré (supprimez-le ou SET STATUS = Z).

La bonne façon de le faire est d'utiliser une seule déclaration. Malheureusement, la syntaxe diffère entre Oracle et SQL Server. La syntaxe SQL Server est la suivante: xxx

Je ne suis pas assez familier avec la clause de retour d'Oracle pour donner un exemple similaire à celui de SQL.

Autres solutions SQL Server nécessitent des indications de verrouillage sur la touche SELECT (avec UNDOCK) pour être correcte. Dans Oracle, l'avenue préférée utilise la mise à jour, mais cela ne fonctionne pas dans SQL Server car pour la mise à jour consiste à être utilisée conjointement avec des curseurs dans SQL.

Dans tous les cas, le comportement que vous avez dans le L'article original est incorrect. Les sessions multiples peuvent tous sélectionner la même ligne (s) et même toutes les mettre à jour, renvoyant le même élément (s) déséquilibré (s) à plusieurs lecteurs.


8 commentaires

Gloire. La sortie est un excellent moyen de faire exactement ce qu'il recherche dans une seule commande.


@Remus: Je vais regarder cela. Nous connaissons souvent le PK - Je ne comptons pas sur le sondage que mon mécanisme de notification en ligne qu'un élément a été soumis à la file d'attente. Le service Lecture de la file d'attente reçoit un message reconduit. La notification RPC inclut le PK, mais pas la charge utile, car la charge utile peut être importante. Cependant, si le service devrait descendre, il doit récupérer de la table. J'essaie d'éliminer une condition de race potentielle, où un service de récupération peut charger une nouvelle entrée de la table et recevoir la notification rectotée.


@Jmarsh: je vois. Si je comprends bien, la plupart des accès seront effectués via le PK, mais de chaque fois (lors d'une récupération de service), un accès sera effectué sur la base de certains autres critères (et d'une commande d'index implicitement). C'est très délicat pour avoir raison, vous pouvez rencontrer une impasse en raison de l'ordre d'accès à l'index, voir rusanu.com/2009/05/16/readwrite-deadlock (sur SQL Server au moins).


@Remus: C'est un article très intéressant! Je pense que cela veut éviter d'utiliser la base de données pour contrôler la concurrence ici. Je considère un algorithme dans le niveau moyen qui n'exige pas CAS dans la DB. J'envisage très fortement, y compris la charge utile dans la notification RPC, et évitez donc l'état de la course tout à fait (peut-être exploiter MSMQ pour fournir la notification et la charge utile).


Assurez-vous de ne pas jeter le bébé avec l'eau du bain. Quels que soient les problèmes de concurrent dans la DB, ils existeront probablement, dans un ou un autre, dans le niveau moyen. Qui fournit vos notifications? Peut-être plutôt que de passer davantage au niveau de la mi-niveau, vous pouvez vous déplacer davantage vers la DB (c.-à-d. Utilisez la messagerie propre de SQL msdn.microsoft.com/en-us/library/ms166104.aspx ).


J'aimerais pouvoir utiliser la messagerie de SQL, mais je dois également soutenir un dos Oracle. Convenait que si je le déplace au niveau moyen, je dois toujours gérer la question de la concurrence, mais j'ai un modèle de code que je crois travaillerait. La section de commentaire sur cette page ne me permet pas assez d'espace pour le décrire efficacement. Si cela fonctionne, je vais le poster sur ce fil comme une réponse complète. Les notifications seront livrées via .NET / WCF (Windows Communication Foundation). Alternativement, je pourrais utiliser MSMQ pour livrer les notifications.


Vous pouvez utiliser WCF avec une chaîne en file d'attente soutenue par MSMQ, est un modèle de programmation légèrement plus facile que Pure MSMQ.


Je pense que c'est la direction que je vais avec ça. Je vais aller de l'avant et marquer cette entrée comme réponse. Merci de parler à travers cela avec moi, Remus.