2011-08-08 5 views
3

J'ai un accès concurrentiel dans un système multi-utilisateur et une procédure stockée comme indiqué ci-dessous:tsql: procédure stockée et tolet

CREATE PROCEDURE dbo.GetWorkitemID 
AS 
DECLARE @workitem int; 

UPDATE workqueue 
SET status = 'InProcess', @workitem = workitemid 
WHERE workitemid = (SELECT TOP 1 workitemid 
FROM workqueue WITH (ROWLOCK,UPDLOCK,READPAST) 
WHERE status = 'New' ORDER BY workitemid) 

SELECT @workitem 

GO 

Il met à jour un statut d'enregistrement unique de « Nouveau » pour « InProcess » et retourne record de ID.

Les questions sont les suivantes: Dois-je utiliser cette procédure stockée dans une étendue de transaction pour activer ROWLOCK, UPDLOCK, etc.? Est-ce nécessaire? Et la seconde: est-ce vraiment thread safe et garantit l'unicité?

Répondre

2
  • Ceci est la bonne façon de fonctionner « table comme une file d'attente »
    Voir ce s'il vous plaît: SQL Server Process Queue Race Condition

  • Vous n'avez pas besoin d'une opération

  • Ce fil est à la fois et la concurrence sécurité

Edit:

Comme un contre-exemple à

de Filip De Vos

Note l'utilisation d'un indice de couverture et UPDLOCK pas XLOCK et la même requête

DROP table locktest 
create table locktest (id int, workitem int, status varchar(50)) 
insert into locktest (id, workitem) values (1, 1), (2,2), (3,3) 
create index ix_test2 on locktest(workitem) INCLUDE (id, status) 

--When I run this on one connection 
begin tran 
select top (1) id, status 
from locktest with (rowlock, updlock, readpast) 
ORDER BY workitem 

... Je me les résultats escomptés dans une autre connexion avec la même question

+0

votre réponse d'origine par le lien? – johnny

+0

@Johnny: oui, mais j'ai ajouté des choses en fonction de vos autres questions – gbn

1

Ce n'est pas fiable. Parce que les indices de verrouillage que vous avez donnés ne sont que des indices de verrouillage. En outre, selon la façon dont la table est indexée, les résultats peuvent être très différents.

Par exemple:

create table test (id int, workitem int, status varchar(50)) 
insert into test (id, workitem) values (1, 1), (2,2), (3,3) 
create index ix_test on test(workitem) 

Quand je lance ce sur une connexion

begin tran 
select * from test with (rowlock, xlock, holdlock) where workitem = 1 

Et je lance ceci sur une seconde connexion:

select top (1) * from test with (rowlock, readpast) order by workitem 

Ce retourne:

workitem 
-------- 
3 

Même si je fais:

update top (1) test with (rowlock, readpast) 
set status = 'Proc' 
output inserted.workitem 

Ainsi, vous pouvez l'utiliser pour en même temps chercher ce dont vous avez besoin, mais ce n'est pas un moyen fiable d'avoir en ordre le traitement simultané.

2

Dois-je utiliser cette procédure stockée dans un champ de transaction ...

Chaque déclaration DML dans SQL exécute dans le contexte d'une transaction, si vous ouvrez explicitement ou non. Par défaut, lors de l'exécution de chaque instruction, le serveur SQL ouvrira une transaction si elle n'est pas ouverte, exécutera l'instruction, puis validera la transaction (si aucune erreur ne s'est produite) ou l'annulera. Sous réserve de l'avertissement par @Filip (qu'il n'y a toujours aucune garantie sur l'ordre dans lequel les articles seront sélectionnés), il sera sûr et chaque invocation retournera une ligne différente, si elle est disponible et non verrouillée.