Disons que vous avez le code suivant (veuillez ignorer que c'est affreux):
BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else
À mon avis, cela ne gère PAS correctement la concurrence. Ce n'est pas parce que vous avez une transaction que quelqu'un d'autre ne lira pas la même valeur que vous aviez avant d'arriver à votre déclaration de mise à jour.
Maintenant, en laissant le code tel quel (je me rends compte que cela est mieux géré comme une seule instruction ou encore mieux en utilisant une colonne d'auto-incrémentation / d'identité), quels sont les moyens sûrs de le faire gérer correctement la concurrence et d'empêcher les conditions de concurrence qui permettent à deux clients d'obtenir la même chose valeur id?
Je suis presque sûr que l'ajout d'un WITH (UPDLOCK, HOLDLOCK)
au SELECT fera l'affaire. Le niveau d'isolement des transactions SERIALIZABLE semble également fonctionner car il interdit à quiconque de lire ce que vous avez fait jusqu'à la fin du transfert ( MISE À JOUR : c'est faux. Voir la réponse de Martin). Est-ce vrai? Vont-ils tous les deux fonctionner aussi bien? Est-ce que l'un est préféré à l'autre?
Imaginez faire quelque chose de plus légitime qu'une mise à jour d'ID - un calcul basé sur une lecture que vous devez mettre à jour. Il pourrait y avoir de nombreuses tables impliquées, dont certaines vous écrirez et d'autres que vous ne ferez pas. Quelle est la meilleure pratique ici?
Après avoir écrit cette question, je pense que les conseils de verrouillage sont meilleurs car vous ne verrouillez que les tables dont vous avez besoin, mais j'apprécierais la contribution de n'importe qui.
PS Et non, je ne connais pas la meilleure réponse et je veux vraiment avoir une meilleure compréhension! :)
update
qui peuvent être basés sur des données obsolètes? Dans ce dernier cas, vous pouvez utiliser larowversion
colonne pour vérifier si la ligne à mettre à jour n'a pas été modifiée depuis sa lecture.Réponses:
Il s'agit simplement de l'
SERIALIZABLE
aspect du niveau d'isolement. Oui, cela fonctionnera, mais avec un risque de blocage.Deux transactions pourront toutes deux lire la ligne simultanément. Ils ne se bloqueront pas car ils prendront un
S
verrou d' objet ou desRangeS-S
verrous d' index en fonction de la structure de la table et ces verrous sont compatibles . Mais ils se bloqueront lorsqu'ils tenteront d'acquérir les verrous nécessaires à la mise à jour (IX
verrou d' objet ou indexRangeS-U
respectivement), ce qui entraînera un blocage.À la place, l'utilisation d'un
UPDLOCK
indice explicite sérialisera les lectures, évitant ainsi le risque de blocage.la source
IX
àX
sur le tas lui-même. Fait intéressant, aucune ligne ne se qualifie donc aucun verrou de ligne n'est retiré. Je ne sais pas pourquoi il prend leX
verrou du tout.Je pense que la meilleure approche pour vous serait d'exposer votre module à une concurrence élevée et de voir par vous-même. Parfois, UPDLOCK seul suffit et il n'est pas nécessaire de HOLDLOCK. Parfois, sp_getapplock fonctionne très bien. Je ne ferais aucune déclaration générale ici - parfois, l'ajout d'un index, d'un déclencheur ou d'une vue indexée supplémentaire modifie le résultat. Nous devons souligner le code de test et voir par nous-mêmes au cas par cas.
Je l' ai écrit plusieurs exemples de tests de stress ici
Edit: pour une meilleure connaissance des internes, vous pouvez lire les livres de Kalen Delaney. Cependant, les livres peuvent se désynchroniser comme toute autre documentation. En outre, il y a trop de combinaisons à considérer: six niveaux d'isolement, de nombreux types de verrous, des index cluster / non cluster et qui sait quoi d'autre. C'est beaucoup de combinaisons. En plus de cela, SQL Server est une source fermée, nous ne pouvons donc pas télécharger le code source, le déboguer, etc. - ce serait la source ultime de connaissances. Tout le reste peut être incomplet ou obsolète après la prochaine version ou le prochain Service Pack.
Donc, vous ne devriez pas décider de ce qui fonctionne pour votre système sans vos propres tests de résistance. Quoi que vous ayez lu, cela peut vous aider à comprendre ce qui se passe, mais vous devez prouver que les conseils que vous avez lus vous conviennent. Je pense que personne ne peut le faire pour vous.
la source
Dans ce cas particulier, l'ajout d'une
UPDLOCK
serrure à laSELECT
borne permettrait en effet d'éviter les anomalies. L'ajout deHOLDLOCK
n'est pas nécessaire car un verrou de mise à jour est maintenu pendant la durée de la transaction, mais j'avoue l'inclure moi-même comme une habitude (peut-être mauvaise) dans le passé.Il n'y a pas de meilleure pratique. Votre choix de contrôle d'accès simultané doit être basé sur les exigences de l'application. Certaines applications / transactions doivent s'exécuter comme si elles détenaient la propriété exclusive de la base de données, évitant à tout prix les anomalies et les inexactitudes. D'autres applications / transactions peuvent tolérer un certain degré d'interférence les unes des autres.
Edit: @ Le commentaire d'AlexKuznetsov m'a incité à relire la question et à supprimer l'erreur très évidente dans ma réponse. Remarque à vous-même sur l'affichage tard dans la nuit.
la source