2009-06-15 15 views
6

Je suis actuellement l'insertion d'un enregistrement dans une table SQL Server, puis en sélectionnant l'ID incrémentation automatique comme suit:Pourquoi le blocage SQL Server suivant est-il inséré lors d'une transaction?

(@p0 int,@p1 nvarchar(8))INSERT INTO [dbo].[Tag]([Some_Int], [Tag]) 
VALUES (@p0, @p1) 

SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value] 

(Ceci a été généré en utilisant LINQ to SQL). Pour une raison quelconque lorsque j'exécute ce code dans une transaction utilisant l'objet TransactionScope avec un niveau d'isolation Serializable, SQL Server génère une erreur de blocage. J'ai analysé les événements de graphique de blocage et a constaté que les deux processus impliqués ont été chacun attend de l'autre pour effectuer l'opération de conversion, comme je comprends les informations suivantes:

<resource-list> 
    <keylock hobtid="72057594101170176" dbid="5" objectname="foo.dbo.Tag" indexname="PK_Tag_1" id="lockb77cdc0" mode="RangeS-S" associatedObjectId="72057594101170176"> 
    <owner-list> 
    <owner id="processc9be40" mode="RangeS-S"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="processc9ae38" mode="RangeI-N" requestType="convert"/> 
    </waiter-list> 
    </keylock> 
    <keylock hobtid="72057594101170176" dbid="5" objectname="foo.dbo.Tag" indexname="PK_Tag_1" id="lockb77cdc0" mode="RangeS-S" associatedObjectId="72057594101170176"> 
    <owner-list> 
    <owner id="processc9ae38" mode="RangeS-S"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="processc9be40" mode="RangeI-N" requestType="convert"/> 
    </waiter-list> 
    </keylock> 
    </resource-list> 

Je crois comprendre que la portée de la transaction empêcherait la deuxième processus d'effectuer l'insertion jusqu'à ce que le premier avait terminé à la fois l'insertion et la sélection de l'identité. Cependant, cela ne semble pas être le cas. Quelqu'un pourrait-il faire la lumière sur la meilleure approche pour atteindre ce dont j'ai besoin d'une manière thread-safe?

- Mise à jour -

Juste à noter; Je suis sûr à 99% qu'une connexion n'est pas partagée entre les deux processus car chacun crée un nouveau DataContext pour communiquer avec la base de données.

- Mise à jour Encore une fois -

Remus Rusanu a souligné que certains renseignements omis était lié au problème, j'ai essayé de simplifier le scénario basé sur le rapport de graphique de blocage, mais j'ai étendu l'explication ici. Avant de faire l'insertion, j'effectue une requête exists sur la table en question pour déterminer si la balise existe déjà. Si c'est le cas, je mets fin à la transaction. Si ce n'est pas le cas, j'effectue une mise à jour, non montrée ici, sur une table qui a pour clé primaire Some_Int, bien que la mise à jour soit purement pour une dernière valeur modifiée. Il peut également être important que la table des balises ait un index cluster composé à la fois de l'identifiant auto inc et de Some_Int. Je ne pensais pas que cette dernière information était pertinente car j'ai essayé de changer la table pour avoir seulement le champ auto inc comme index clé/cluster en vain.

Merci.

+0

Putain bonne question! Je veux voir une réponse. – Chris

Répondre

7

Le 'converti' en question est un 'lock convert' from RangeS-S to RangeI-N, sans aucune relation avec la fonction 'CONVERT'. Le fait que vous ayez des verrous RangeS-S déjà placés sur l'index PK_Tag_1 indique que vous faites quelque chose de plus qu'un INSERT. Est-ce que votre transaction vérifie d'abord si le nouvel enregistrement 'existe' avant de tenter l'insertion?

+0

en effet, il le fait ... – LaserJesus

+0

J'ai mis à jour la question pour refléter la situation plus complètement – LaserJesus

+0

J'ai changé le niveau d'isolement en instantané, ce qui semble avoir allégé mes problèmes d'interblocage. Merci pour votre aide :) – LaserJesus

-1

Vous n'avez pas besoin d'une opération du tout. La fonction scope_identity() renvoie l'ID créé en dernier dans la même portée, donc il n'y a pas de problème si une autre insertion est exécutée avant que vous obteniez l'ID, car cela est dans une portée différente.

+0

La portée de la transaction est telle que je peux renvoyer les modifications si quelque chose se passe mal ailleurs, ou au moins, lorsque le test uni se termine. – LaserJesus

+0

@downvoter: Pourquoi la downvote? Si vous ne dites pas ce que vous pensez être faux, cela ne peut en aucun cas améliorer la réponse. – Guffa

0

Vérifiez le isolationLevel, qui est utilisé dans votre requête. Notez que TransactionScope utilise Serializable niveau d'isolation par défaut (http://msdn.microsoft.com/en-us/library/ms172152.aspx). Essayez de modifier le niveau d'isolation de votre transaction en lecture validée.