Domanda

Ho il grafico seguente situazione di stallo che descrive due istruzioni SQL che vengono blocco critico tra loro. Non sono solo sicuro di come analizzare questo problema e poi sistemare il mio codice SQL per evitare che ciò accada.

principale grafico deadlock

alt text http://img140.imageshack.us/img140/6193/deadlock1 .png Clicca qui per un'immagine più grande.

Lato sinistro, informazioni

alt text http://img715.imageshack.us/img715/3999/deadlock2 .png Clicca qui per un'immagine più grande.

Lato destro, informazioni

alt text http://img686.imageshack.us/img686/5097/deadlock3 .png Clicca qui per un'immagine più grande.

file XML grezzo Deadlock Schema

Clicca qui per scaricare il file xml .

schema della tabella

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

Dettagli

LogEntries tabella

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

Dettagli

client connessi tabella

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

Qual è il codice di fare?

che sto leggendo in un certo numero di file (ad es. 3 permette di dire, per questo esempio) al tempo stesso . Ogni file contiene dati diversi ma lo stesso tipo di dati. Ho poi inserire i dati nella tabella LogEntries e poi (se necessario) mi inserire o cancellare qualcosa dal tavolo ConnectedClients.

Ecco il mio codice 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();
}

Ora ogni file ha il proprio esempio UnitOfWork (il che significa che ha la sua propria connessione al database, delle transazioni e il contesto repository). Così sto supponendo che questo significa che c'è 3 diverse connessioni al DB tutto accade allo stesso tempo.

Infine, questo è utilizzando Entity Framework come repository, ma per favore non lasciare che ti impedisce di avere un pensare a questo problema .

Utilizzando uno strumento di profiling, il Isolation Level è Serializable. Ho anche provato ReadCommited e ReadUncommited, ma entrambi di errore: -

  • ReadCommited: come sopra. Deadlock.
  • ReadUncommited: errore diverso. eccezione EF che dice di aspettarsi qualche risultato di nuovo, ma non ha ottenuto nulla. Sto indovinando questo è il LogEntryId Identità (scope_identity) valore che si prevede, ma non recupera per la lettura sporca.

Si prega di aiutare!

PS. E 'SQL Server 2008, btw.


Aggiornamento # 2

Dopo aver letto Remo Ruşanu s ' risposta aggiornato, ho sentito che avrei potuto cercare di fornire un po' più informazioni per vedere se qualcun altro può ulteriore aiuto.

EF Schema

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

Ora, Remus suggerisce (e la nota, lo fa dire che è poco familiare con EF) ...

  

L'ultimo pezzo del puzzle, il   serratura sinistra inspiegabile nodo ha sulla   PK_ConnectedClients, io presumo sia   dall'attuazione EF   InsertOrUpdate. Probabilmente fa un   ricerca prima, ea causa del FK   rapporto tra il dichiarato   ConnectedClients e LogEntries, è   cerca sulla PK_ConnectedClients, quindi   acquisire la serratura serializzabile.

Interessante. Non sono sicuro che il motivo per cui il nodo di sinistra ha un blocco su PK_ConnectedClients, come suggerito sopra. Ok, consente di controllare il codice per quel metodo ....

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. si tratta di un semplice AddObject (aka. Insert) o Attach (aka. Update). Nessun riferimento. codice SQL, inoltre, non accenno di qualsiasi materiale di ricerca.

Ok allora ... Io ho altri due metodi ... forse stanno facendo alcune ricerche?

In ConnectedClientRepository ...

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

    Context.ConnectedClients.AddObject(connectedClient);
}

No -.> Anche di base, come

fortunato ultimo metodo? Wow .. Ora questo è interessante ....

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);
    }
}

Così, guardando al di sopra, prendo un'istanza del record desidero eliminare .. e se esiste, quindi eliminarlo.

Quindi .. se io commento che chiamata di metodo, nella mia logica iniziale strada fino alla cima di questo SO post ... cosa succede?

funziona. WOWZ.

Funziona anche come sia Serializable o Read Commited - sia il lavoro quando io non chiamo il metodo Delete

.

Quindi, perché che eliminare il metodo essere sempre una serratura? È becuase select (con serializable) fa un blocco e un po 'di stallo succede?

Con read committed, è possibile che ho 3 chiamate alla cancellazione accadono allo stesso tempo.

  • 1 ° afferra un'istanza dei dati.
  • 2 ° (e 3 °) afferra un'altra istanza dei dati stessi.
  • Ora, prima di cancellare. multa.
  • 2 ° cancella .. ma la fila è andato .. in modo da quindi io ottenere questo errore strano su influenzato un numero inaspettato di righe (0). <== a zero elementi eliminati.

Possibile? Se è così .. ehm ... come posso risolvere questo problema? Si tratta di un classico caso di una condizione di competizione? E 'possibile evitare che ciò happeneing in qualche modo?


Aggiornamenti

  • fissi i link alle immagini.
  • Collegamento al file XML stallo crudo. Qui è lo stesso link .
  • aggiunta schema della tabella di database.
  • aggiunto sia i dettagli della tabella.
È stato utile?

Soluzione

Il nodo lato sinistro è in possesso di un RangeS-U lock su PK_CustomerRecords e vuole un blocco RangeS-U su i1 (presumo sua un indice su LogEntries). Il nodo lato destro ha un blocco RangeS-U su i1 e vuole un RangeI-N su PK_CustomerRecords.

A quanto pare la situazione di stallo si verifica tra il _logEntriesRepository.InsertOrUpdate (nodo di sinistra) e _connectedClientRepository.Insert (nodo a destra). Senza conoscere il tipo di relazioni EF dichiarati, non posso commentare perché il nodo di sinistra hanno una serratura PK_CustomerRecords nel momento in cui inserisce il LogEntry. Ritengo sospetto che questo è causato da un comportamento di tipo ORM indotta da EF, come ricerca di un membro 'precaricata', o può essere causato da un TransactionScope livello superiore che circonda la portata nel codice snipped inviato.

Come altri hanno già detto, è necessario inviare lo schema del database in una valutazione situazione di stallo, perché il percorso di accesso (indici utilizzati) è critica. Vedere il mio articolo lettura e scrittura stallo per una discussione più dettagliata sulle conseguenze di indici in stallo.

La mia prima raccomandazione sarebbe quella di forzare la portata transazione da read committed. Il livello predefinito di serializzabile TransactionScopes non è quasi mai necessario, in pratica, è un maiale di prestazioni, e in questo particolare caso annunci un sacco di rumori inutili alle indagini stallo portando i blocchi di intervallo nell'equazione, complicando tutto. Si prega di inviare le informazioni deadlock che si verifica in lettura commesso.

Inoltre, non postare una foto del grafico stallo. Un'immagine vale più di mille parole, non è vero qui, inserire il codice XML stallo originale:. Ha un sacco di informazioni non visibili nelle belle immagini

Aggiorna

Dal XML stallo posso vedere che il nodo di sinistra è in esecuzione insert [dbo].[LogEntries]([GameFileId], [CreatedOn], [EventTypeId], [Message], [Code], [Violation], [ClientName], [ClientGuid], [ClientIpAndPort]) values (@0, @1, @2, null, null, null, @3, @4, @5) (l'elemento <executionStack><frame>). Ma ancora più importante, posso vedere l'oggetto dietro l'indice misterioso 'i1': objectname="AWing.sys.fulltext_index_docidstatus_1755869322" indexname="i1". Così la situazione di stallo si verifica su un indice full-text .

Quindi la spiegazione completa di stallo è:

  • nodo destra è _connectedClientRepository.Insert, è necessario un blocco inserto sull'astina PK_ConnectedClients. Ha un blocco gamme-U sulla i1 indice full-text da precedentemente esecuzione _logEntryRepository.InsertOrUpdate.
  • nodo sinistra è al _logEntryRepository.InsertOrUpdate, al INSERT all'interno del lotto, e ha bisogno di una serratura gamme-U sulla i1 indice full-text. Ha un blocco gamme-S sul PK_ConnectedClients che blocca il nodo di destra, e questo non si spiega con qualsiasi cosa nel XML grafico.

L'ultimo pezzo del puzzle, il blocco inspiegabile lasciato nodo ha sulle PK_ConnectedClients, io presumo sia dalla realizzazione di EF InsertOrUpdate. Probabilmente fa una ricerca prima, ed a causa del rapporto FK dichiarata tra ConnectedClients e LogEntries, cerca il PK_ConnectedClients, quindi acquisire il blocco serializzabile.

Il principale colpevole qui è il livello di isolamento delle transazioni (Serializable) ed il comportamento EF su InsertOrUpdate. Non posso dare consigli sul comportamento EF, ma il livello serializzabile è eccessivo di sicuro. Il che ci riporta al l'errore che si ottiene sotto il livello di lettura impegnata, che, purtroppo, è ancora una volta un errore EF io non posso commentare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top