Question

J'ai le graphique de blocage suivant qui décrit deux instructions SQL qui sont supercondamnation les uns des autres. Je ne suis pas sûr de savoir comment analyser ce problème et corriger mon code SQL pour empêcher que cela se produise.

graphique de blocage principal

texte alt http://img140.imageshack.us/img140/6193/deadlock1 .png Cliquez ici pour une image plus grande.

Côté gauche, les détails

texte alt http://img715.imageshack.us/img715/3999/deadlock2 .png Cliquez ici pour une image plus grande.

Côté droit, les détails

texte alt http://img686.imageshack.us/img686/5097/deadlock3 .png Cliquez ici pour une image plus grande.

Raw Deadlock schéma fichier xml

Cliquez ici pour télécharger le fichier xml .

Tableau schéma

texte alt http://img509.imageshack.us/img509/5843/deadlockschema .png

LogEntries détails de la table

texte alt http://img28.imageshack.us/img28/9732/deadlocklogentriestable .png

Détails Tableau Clients connectés

texte alt http://img11.imageshack.us/img11/7681/deadlockconnectedclient .png

Quel est le code faire?

Je lis dans un certain nombre de fichiers (par exemple. 3 permet de dire, pour cet exemple) en même temps . Chaque fichier contient des données différentes mais le même type de données. Insérer ensuite les données dans la table de LogEntries puis (si nécessaire) insérer ou supprimer quelque chose de la table ConnectedClients.

Voici mon code sql.

using (TransactionScope transactionScope = new TransactionScope())
{
    _logEntryRepository.InsertOrUpdate(logEntry);

    // Now, if this log entry was a NewConnection or an LostConnection, then we need to make sure we update the ConnectedClients.
    if (logEntry.EventType == EventType.NewConnection)
    {
        _connectedClientRepository.Insert(new ConnectedClient { LogEntryId = logEntry.LogEntryId });
     }

    // A (PB) BanKick does _NOT_ register a lost connection .. so we need to make sure we handle those scenario's as a LostConnection.
    if (logEntry.EventType == EventType.LostConnection ||
        logEntry.EventType == EventType.BanKick)
    {
        _connectedClientRepository.Delete(logEntry.ClientName, logEntry.ClientIpAndPort);
    }

    _unitOfWork.Commit();
    transactionScope.Complete();
}

Maintenant, chaque fichier a sa propre instance de UnitOfWork (ce qui signifie qu'il a sa propre connexion de base de données, transaction et contexte référentiel). Je suppose donc que cela signifie qu'il ya 3 connexions différentes à la DB tout se passe en même temps.

Enfin, est d'utiliser Entity Framework comme référentiel, mais s'il vous plaît ne laissez pas vous empêcher d'avoir un penser à ce problème .

L'utilisation d'un outil de profilage, le Isolation Level est Serializable. J'ai aussi essayé ReadCommited et ReadUncommited, mais ils ont tous deux erreurs: -

  • ReadCommited: comme ci-dessus. Deadlock.
  • ReadUncommited: autre erreur. EF exception qui dit qu'il attendait un résultat de retour, mais n'a rien. Je devine que c'est le LogEntryId Identité (scope_identity) valeur qui est attendue mais pas récupérer en raison de la lecture sale.

S'il vous plaît aider!

PS. Il est Sql Server 2008, btw.


Mise à jour # 2

Après avoir lu Remus Rusanu de réponse mis à jour, je sentais que je pouvais essayer de fournir un peu plus d'informations pour voir si quelqu'un d'autre peut obtenir de l'aide.

EF Schéma

texte alt http://img691.imageshack.us/img691/600/deadlockefmodel .png

Maintenant, Remus suggère (et la note, il ne dit qu'il est peu familier avec EF) ...

  

Le dernier morceau du casse-tête, la   Serrure inexpliquée noeud gauche a sur le   PK_ConnectedClients, je suppose est   de la mise en œuvre de EF   InsertOrUpdate. Il fait probablement   recherche d'abord, et à cause de la FK   relation entre déclarée   ConnectedClients et LogEntries, il   cherche à PK_ConnectedClients, par conséquent   acquérir le verrou serializable.

Intéressant

. Je ne sais pas pourquoi le noeud gauche a un verrou sur PK_ConnectedClients, comme suggéré ci-dessus. Ok, permet de vérifier le code pour cette méthode ....

public void InsertOrUpdate(LogEntry logEntry)
{
    LoggingService.Debug("About to InsertOrUpdate a logEntry");

    logEntry.ThrowIfArgumentIsNull("logEntry");

    if (logEntry.LogEntryId <= 0)
    {
        LoggingService.Debug("Current logEntry instance doesn't have an Id. Instance object will be 'AddObject'.");
        Context.LogEntries.AddObject(logEntry);
    }
    else
    {
        LoggingService.Debug("Current logEntry instance has an Id. Instance object will be 'Attached'.");
        Context.LogEntries.Attach(logEntry);
    }
}

Hmm. il est un simple AddObject (aka. Insérer) ou Attach (aka. Mise à jour). Aucune référence. Code Sql ne laisse pas entrevoir aussi de toute substance de recherche.

Ok alors ... j'ai deux autres méthodes ... peut-être qu'ils sont en train de faire des recherches?

ConnectedClientRepository ...

public void Insert(ConnectedClient connectedClient)
{
    connectedClient.ThrowIfArgumentIsNull("connectedClient");

    Context.ConnectedClients.AddObject(connectedClient);
}

Non -.> Également de base,

chanceux dernière méthode? Wow .. maintenant ce qui est intéressant ....

public void Delete(string clientName, string clientIpAndPort)
{
    clientName.ThrowIfArgumentIsNullOrEmpty("clientName");
    clientIpAndPort.ThrowIfArgumentIsNullOrEmpty("clientIpAndPort");

    // First we need to attach this object to the object manager.
    var existingConnectedClient = (from x in GetConnectedClients()
                                   where x.LogEntry.ClientName == clientName.Trim() &&
                                   x.LogEntry.ClientIpAndPort == clientIpAndPort.Trim() &&
                                   x.LogEntry.EventTypeId == (byte)EventType.NewConnection
                                   select x)
                                  .Take(1)
                                  .SingleOrDefault();

    if (existingConnectedClient != null)
    {
        Context.ConnectedClients.DeleteObject(existingConnectedClient);
    }
}

Alors, en regardant ci-dessus, je prends une instance du dossier que je souhaite supprimer .. et si elle existe, puis supprimez-le.

.. si je commente que l'appel de la méthode, dans ma logique initiale chemin jusqu'au sommet de ce poste SO ... ce qui se passe?

cela fonctionne. WOWZ.

Il fonctionne aussi comme soit Serializable ou Read Commited - à la fois le travail quand je ne l'appelle pas la méthode Delete

.

Alors, pourquoi cette méthode supprimer être obtenir un verrou? Est-il Becuase la sélection (avec serializable) fait une serrure et une impasse qui se passe?

Avec read committed, est-il possible que j'ai 3 appels à la suppression se produisent en même temps.

  • 1er saisit une instance des données.
  • 2 (et 3) saisit une autre instance du même données.
  • Maintenant, 1er supprimer son. très bien.
  • 2 supprime .. mais la ligne a disparu .. Ainsi donc je reçois cette erreur bizarre affecté un nombre inattendu de lignes (0). <== zéro éléments supprimés.

Possible? Si oui .. euh ... comment puis-je résoudre ce problème? Est-ce un cas classique d'une condition de course? Est-il possible d'empêcher que cela happeneing en quelque sorte?


Mises à jour

  • Correction des liens vers les images.
  • Lien vers le fichier de blocage XML brut. Ici est le même lien .
  • schéma de la table de base de données Ajouté.
  • Ajout les détails de la table.
Était-ce utile?

La solution

Le nœud côté gauche tient un RangeS-U lock sur PK_CustomerRecords et veut un verrou de RangeS-U sur i1 (je suppose que son index sur un LogEntries). Le nœud côté droit a un verrou RangeS-U sur i1 et veut un RangeI-N sur PK_CustomerRecords.

Apparemment, le blocage se produit entre le _logEntriesRepository.InsertOrUpdate (noeud gauche), et _connectedClientRepository.Insert (noeud droit). Sans connaître le type de relations EF déclaré, je ne peux pas commenter pourquoi le nœud du côté gauche ont un verrou sur PK_CustomerRecords au moment où il insère le LogEntry. Je soupçonne que cela est causé soit par un comportement de type ORM induite par EF, comme recherche d'un « pré-chargé », ou elle peut être causée par un niveau plus élevé TransactionScope qui entoure le champ d'application dans le code Snipped affiché.

Comme d'autres ont dit, il est nécessaire d'afficher le schéma de base de données dans l'évaluation du blocage, car le chemin d'accès (indices utilisés) est essentielle. Voir mon article lecture-écriture impasse pour une discussion plus détaillée sur l'implication des indices des interblocages.

Ma première recommandation serait de forcer le champ de transaction à read committed. La valeur par défaut niveau sérialisable de TransactionScopes est presque jamais nécessaire dans la pratique, est un porc de performance, et dans ce cas particulier des annonces beaucoup de bruit inutile à l'enquête de l'impasse en apportant les serrures de gamme dans l'équation, tout ce qui complique. S'il vous plaît poster les informations de blocage qui se produit en lecture commis.

En outre, ne postez pas une image du graphique de blocage. Une image dit est faux ici, poster XML de blocage d'origine mille mots. Il a beaucoup d'informations non visibles sur les jolies images

Mise à jour

A partir du XML de l'impasse, je peux voir que le nœud gauche exécute insert [dbo].[LogEntries]([GameFileId], [CreatedOn], [EventTypeId], [Message], [Code], [Violation], [ClientName], [ClientGuid], [ClientIpAndPort]) values (@0, @1, @2, null, null, null, @3, @4, @5) (l'élément <executionStack><frame>). Mais plus important encore, je peux voir l'objet derrière l'indice misterious « i1 »: objectname="AWing.sys.fulltext_index_docidstatus_1755869322" indexname="i1". Ainsi, le blocage se produit sur une index de texte intégral .

Donc, l'explication complète de l'impasse est:

  • noeud droit est à _connectedClientRepository.Insert, il a besoin d'un verrou d'insertion de la plage sur PK_ConnectedClients. Il a un verrou Ranges-U sur l'index du texte intégral de l'exécution précédemment i1 _logEntryRepository.InsertOrUpdate.
  • noeud gauche est au _logEntryRepository.InsertOrUpdate, à l'instruction INSERT à l'intérieur du lot, et il a besoin d'un verrou Ranges-U sur l'indice fulltext i1. Il dispose d'un verrou Ranges-S sur PK_ConnectedClients qui bloque le nœud droit, et ce n'est pas expliqué par quoi que ce soit dans le fichier XML graphique.

Le dernier morceau du casse-tête, le verrou gauche inexpliqué noeud a sur les PK_ConnectedClients, je suppose est de la mise en œuvre de EF de InsertOrUpdate. Il fait sans doute une recherche d'abord, et à cause de la relation FK déclarée entre ConnectedClients et LogEntries, il cherche à PK_ConnectedClients, l'acquisition d'où le verrou sérialisable.

Le principal coupable ici sont le niveau d'isolation des transactions (Serializable) et le comportement EF sur InsertOrUpdate. Je ne peux pas donner des conseils sur le comportement EF, mais le niveau sérialisable est surpuissant pour sûr. Ce qui nous ramène à l'erreur que vous obtenez en lecture niveau engagé, qui, malheureusement, est encore une erreur EF, je ne peux pas commenter.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top