SQL Deadlock sulla stessa chiave cluster esclusivamente bloccata (con NHibernate) in Elimina / Inserisci


29

Lavoro a questo problema di deadlock ormai da alcuni giorni e, qualunque cosa faccia, persiste in un modo o nell'altro.

Innanzitutto, la premessa generale: abbiamo visite con VisitItems in una relazione uno a molti.

Informazioni pertinenti su VisitItems:

CREATE TABLE [BAR].[VisitItems] (
    [Id]                INT             IDENTITY (1, 1) NOT NULL,
    [VisitType]         INT             NOT NULL,
    [FeeRateType]       INT             NOT NULL,
    [Amount]            DECIMAL (18, 2) NOT NULL,
    [GST]               DECIMAL (18, 2) NOT NULL,
    [Quantity]          INT             NOT NULL,
    [Total]             DECIMAL (18, 2) NOT NULL,
    [ServiceFeeType]    INT   NOT NULL,
    [ServiceText]       NVARCHAR (200)  NULL,
    [InvoicingProviderId] INT   NULL,
    [FeeItemId]        INT             NOT NULL,
    [VisitId]          INT             NULL,
    [IsDefault] BIT NOT NULL DEFAULT 0, 
    [SourceVisitItemId] INT NULL, 
    [OverrideCode] INT NOT NULL DEFAULT 0, 
    [InvoiceToCentre] BIT NOT NULL DEFAULT 0, 
    [IsSurchargeItem] BIT NOT NULL DEFAULT 0, 
    CONSTRAINT [PK_BAR.VisitItems] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_BAR.VisitItems_BAR.FeeItems_FeeItem_Id] FOREIGN KEY ([FeeItemId]) REFERENCES [BAR].[FeeItems] ([Id]),
    CONSTRAINT [FK_BAR.VisitItems_BAR.Visits_Visit_Id] FOREIGN KEY ([VisitId]) REFERENCES [BAR].[Visits] ([Id]), 
    CONSTRAINT [FK_BAR.VisitItems_BAR.VisitTypes] FOREIGN KEY ([VisitType]) REFERENCES [BAR].[VisitTypes]([Id]), 
    CONSTRAINT [FK_BAR.VisitItems_BAR.FeeRateTypes] FOREIGN KEY ([FeeRateType]) REFERENCES [BAR].[FeeRateTypes]([Id]),
    CONSTRAINT [FK_BAR.VisitItems_CMN.Users_Id] FOREIGN KEY (InvoicingProviderId) REFERENCES [CMN].[Users] ([Id]),
    CONSTRAINT [FK_BAR.VisitItems_BAR.VisitItems_SourceVisitItem_Id] FOREIGN KEY ([SourceVisitItemId]) REFERENCES [BAR].[VisitItems]([Id]),
    CONSTRAINT [CK_SourceVisitItemId_Not_Equal_Id] CHECK ([SourceVisitItemId] <> [Id]),
    CONSTRAINT [FK_BAR.VisitItems_BAR.OverrideCodes] FOREIGN KEY ([OverrideCode]) REFERENCES [BAR].[OverrideCodes]([Id]),
    CONSTRAINT [FK_BAR.VisitItems_BAR.ServiceFeeTypes] FOREIGN KEY ([ServiceFeeType]) REFERENCES [BAR].[ServiceFeeTypes]([Id])
)

CREATE NONCLUSTERED INDEX [IX_FeeItem_Id]
    ON [BAR].[VisitItems]([FeeItemId] ASC)

CREATE NONCLUSTERED INDEX [IX_Visit_Id]
    ON [BAR].[VisitItems]([VisitId] ASC)

Informazioni sulla visita:

CREATE TABLE [BAR].[Visits] (
    [Id]                     INT            IDENTITY (1, 1) NOT NULL,
    [VisitType]              INT            NOT NULL,
    [DateOfService]          DATETIMEOFFSET  NOT NULL,
    [InvoiceAnnotation]      NVARCHAR(255)  NULL ,
    [PatientId]              INT            NOT NULL,
    [UserId]                 INT            NULL,
    [WorkAreaId]             INT            NOT NULL, 
    [DefaultItemOverride] BIT NOT NULL DEFAULT 0, 
    [DidNotWaitAdjustmentId] INT NULL, 
    [AppointmentId] INT NULL, 
    CONSTRAINT [PK_BAR.Visits] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_BAR.Visits_CMN.Patients] FOREIGN KEY ([PatientId]) REFERENCES [CMN].[Patients] ([Id]) ON DELETE CASCADE,
    CONSTRAINT [FK_BAR.Visits_CMN.Users] FOREIGN KEY ([UserId]) REFERENCES [CMN].[Users] ([Id]),
    CONSTRAINT [FK_BAR.Visits_CMN.WorkAreas_WorkAreaId] FOREIGN KEY ([WorkAreaId]) REFERENCES [CMN].[WorkAreas] ([Id]), 
    CONSTRAINT [FK_BAR.Visits_BAR.VisitTypes] FOREIGN KEY ([VisitType]) REFERENCES [BAR].[VisitTypes]([Id]),
    CONSTRAINT [FK_BAR.Visits_BAR.Adjustments] FOREIGN KEY ([DidNotWaitAdjustmentId]) REFERENCES [BAR].[Adjustments]([Id]), 
);

CREATE NONCLUSTERED INDEX [IX_Visits_PatientId]
    ON [BAR].[Visits]([PatientId] ASC);

CREATE NONCLUSTERED INDEX [IX_Visits_UserId]
    ON [BAR].[Visits]([UserId] ASC);

CREATE NONCLUSTERED INDEX [IX_Visits_WorkAreaId]
    ON [BAR].[Visits]([WorkAreaId]);

Più utenti desiderano aggiornare contemporaneamente la tabella VisitItems nel modo seguente:

Una richiesta web separata creerà una visita con VisitItems (in genere 1). Quindi (la richiesta del problema):

  1. La richiesta Web arriva, apre la sessione NHibernate, avvia la transazione NHibernate (utilizzando la lettura ripetibile con READ_COMMITTED_SNAPSHOT attivato).
  2. Leggi tutti gli elementi della visita per una determinata visita di VisitId .
  3. Il codice valuta se gli articoli sono ancora pertinenti o se ne abbiamo bisogno di nuovi utilizzando regole complesse (con una durata leggermente lunga, ad esempio 40 ms).
  4. Il codice trova 1 elemento da aggiungere, lo aggiunge utilizzando NHibernate Visit.VisitItems.Add (..)
  5. Il codice identifica che un elemento deve essere eliminato (non quello che abbiamo appena aggiunto), lo rimuove utilizzando NHibernate Visit.VisitItems.Remove (elemento).
  6. Il codice commette la transazione

Con uno strumento simulo 12 richieste simultanee che è molto probabile che accada in un futuro ambiente di produzione.

[EDIT] Su richiesta, rimosso molti dei dettagli dell'indagine che avevo aggiunto qui per farla breve.

Dopo molte ricerche, il passo successivo è stato pensare a un modo in cui posso bloccare il suggerimento su un indice diverso da quello usato nella clausola where (ovvero la chiave primaria, dal momento che viene utilizzata per l'eliminazione), quindi ho modificato la mia istruzione lock in :

var items = (List<VisitItem>)_session.CreateSQLQuery(@"SELECT * FROM BAR.VisitItems WITH (XLOCK, INDEX([PK_BAR.VisitItems]))
        WHERE VisitId = :visitId")
        .AddEntity(typeof(VisitItem))
        .SetParameter("visitId", qi.Visit.Id)
        .List<VisitItem>();

Ciò ha ridotto leggermente i deadlock in frequenza, ma stavano ancora accadendo. Ed ecco dove sto iniziando a perdersi:

Tre serrature esclusive?

<deadlock-list>
  <deadlock victim="process3f71e64e8">
    <process-list>
      <process id="process3f71e64e8" taskpriority="0" logused="0" waitresource="KEY: 5:72057594071744512 (a5e1814e40ba)" waittime="3812" ownerId="8004520" transactionname="user_transaction" lasttranstarted="2015-12-14T10:24:58.010" XDES="0x3f7cb43b0" lockMode="X" schedulerid="1" kpid="15788" status="suspended" spid="63" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2015-12-14T10:24:58.013" lastbatchcompleted="2015-12-14T10:24:58.013" lastattention="1900-01-01T00:00:00.013" clientapp=".Net SqlClient Data Provider" hostname="ABC" hostpid="10016" loginname="bsapp" isolationlevel="repeatable read (3)" xactid="8004520" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
        <executionStack>
          <frame procname="adhoc" line="1" stmtstart="18" stmtend="254" sqlhandle="0x0200000024a9e43033ef90bb631938f939038627209baafb0000000000000000000000000000000000000000">
            unknown
          </frame>
          <frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
            unknown
          </frame>
        </executionStack>
        <inputbuf>
          (@p0 int)SELECT * FROM BAR.VisitItems WITH (XLOCK, INDEX([PK_BAR.VisitItems]))
          WHERE VisitId = @p0
        </inputbuf>
      </process>
      <process id="process4105af468" taskpriority="0" logused="1824" waitresource="KEY: 5:72057594071744512 (8194443284a0)" waittime="3792" ownerId="8004519" transactionname="user_transaction" lasttranstarted="2015-12-14T10:24:58.010" XDES="0x3f02ea3b0" lockMode="S" schedulerid="8" kpid="15116" status="suspended" spid="65" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2015-12-14T10:24:58.033" lastbatchcompleted="2015-12-14T10:24:58.033" lastattention="1900-01-01T00:00:00.033" clientapp=".Net SqlClient Data Provider" hostname="ABC" hostpid="10016" loginname="bsapp" isolationlevel="repeatable read (3)" xactid="8004519" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
        <executionStack>
          <frame procname="adhoc" line="1" stmtstart="18" stmtend="98" sqlhandle="0x0200000075abb0074bade5aa57b8357410941428df4d54130000000000000000000000000000000000000000">
            unknown
          </frame>
          <frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
            unknown
          </frame>
        </executionStack>
        <inputbuf>
          (@p0 int)DELETE FROM BAR.VisitItems WHERE Id = @p0
        </inputbuf>
      </process>
    </process-list>
    <resource-list>
      <keylock hobtid="72057594071744512" dbid="5" objectname="BAR.VisitItems" indexname="PK_BAR.VisitItems" id="lock449e27500" mode="X" associatedObjectId="72057594071744512">
        <owner-list>
          <owner id="process4105af468" mode="X"/>
        </owner-list>
        <waiter-list>
          <waiter id="process3f71e64e8" mode="X" requestType="wait"/>
        </waiter-list>
      </keylock>
      <keylock hobtid="72057594071744512" dbid="5" objectname="BAR.VisitItems" indexname="PK_BAR.VisitItems" id="lock46a525080" mode="X" associatedObjectId="72057594071744512">
        <owner-list>
          <owner id="process3f71e64e8" mode="X"/>
        </owner-list>
        <waiter-list>
          <waiter id="process4105af468" mode="S" requestType="wait"/>
        </waiter-list>
      </keylock>
    </resource-list>
  </deadlock>
</deadlock-list>

Una traccia del numero risultante di query è simile alla seguente.
[EDIT] Whoa. Che settimana. Ora ho aggiornato la traccia con la traccia non modificata dell'affermazione pertinente che penso porti al deadlock.

exec sp_executesql N'SELECT * FROM BAR.VisitItems WITH (XLOCK, INDEX([PK_BAR.VisitItems]))
                WHERE VisitId = @p0',N'@p0 int',@p0=3826
go
exec sp_executesql N'SELECT visititems0_.VisitId as VisitId1_, visititems0_.Id as Id1_, visititems0_.Id as Id37_0_, visititems0_.VisitType as VisitType37_0_, visititems0_.FeeItemId as FeeItemId37_0_, visititems0_.FeeRateType as FeeRateT4_37_0_, visititems0_.Amount as Amount37_0_, visititems0_.GST as GST37_0_, visititems0_.Quantity as Quantity37_0_, visititems0_.Total as Total37_0_, visititems0_.ServiceFeeType as ServiceF9_37_0_, visititems0_.ServiceText as Service10_37_0_, visititems0_.InvoiceToCentre as Invoice11_37_0_, visititems0_.IsDefault as IsDefault37_0_, visititems0_.OverrideCode as Overrid13_37_0_, visititems0_.IsSurchargeItem as IsSurch14_37_0_, visititems0_.VisitId as VisitId37_0_, visititems0_.InvoicingProviderId as Invoici16_37_0_, visititems0_.SourceVisitItemId as SourceV17_37_0_ FROM BAR.VisitItems visititems0_ WHERE visititems0_.VisitId=@p0',N'@p0 int',@p0=3826
go
exec sp_executesql N'INSERT INTO BAR.VisitItems (VisitType, FeeItemId, FeeRateType, Amount, GST, Quantity, Total, ServiceFeeType, ServiceText, InvoiceToCentre, IsDefault, OverrideCode, IsSurchargeItem, VisitId, InvoicingProviderId, SourceVisitItemId) VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15); select SCOPE_IDENTITY()',N'@p0 int,@p1 int,@p2 int,@p3 decimal(28,5),@p4 decimal(28,5),@p5 int,@p6 decimal(28,5),@p7 int,@p8 nvarchar(4000),@p9 bit,@p10 bit,@p11 int,@p12 bit,@p13 int,@p14 int,@p15 int',@p0=1,@p1=452,@p2=1,@p3=0,@p4=0,@p5=1,@p6=0,@p7=1,@p8=NULL,@p9=0,@p10=1,@p11=0,@p12=0,@p13=3826,@p14=3535,@p15=NULL
go
exec sp_executesql N'UPDATE BAR.Visits SET VisitType = @p0, DateOfService = @p1, InvoiceAnnotation = @p2, DefaultItemOverride = @p3, AppointmentId = @p4, ReferralRequired = @p5, ReferralCarePlan = @p6, UserId = @p7, PatientId = @p8, WorkAreaId = @p9, DidNotWaitAdjustmentId = @p10, ReferralId = @p11 WHERE Id = @p12',N'@p0 int,@p1 datetimeoffset(7),@p2 nvarchar(4000),@p3 bit,@p4 int,@p5 bit,@p6 nvarchar(4000),@p7 int,@p8 int,@p9 int,@p10 int,@p11 int,@p12 int',@p0=1,@p1='2016-01-22 12:37:06.8915296 +08:00',@p2=NULL,@p3=0,@p4=NULL,@p5=0,@p6=NULL,@p7=3535,@p8=4246,@p9=2741,@p10=NULL,@p11=NULL,@p12=3826
go
exec sp_executesql N'DELETE FROM BAR.VisitItems WHERE Id = @p0',N'@p0 int',@p0=7919
go

Ora il mio lucchetto sembra avere un effetto poiché viene mostrato nel grafico del deadlock. Ma cosa? Tre blocchi esclusivi e un blocco condiviso? Come funziona sullo stesso oggetto / chiave? Ho pensato che fintanto che hai un lucchetto esclusivo, non puoi ottenere un lucchetto condiviso da qualcun altro? E viceversa. Se hai un blocco condiviso, nessuno può ottenere un blocco esclusivo, devono aspettare.

Penso che mi manchi una comprensione più profonda qui su come funzionano i blocchi quando vengono presi su più chiavi sullo stesso tavolo.

Ecco alcune delle cose che ho provato e il loro impatto:

  • Aggiunto un altro suggerimento indice su IX_Visit_Id all'istruzione lock. Nessun cambiamento
  • Aggiunta una seconda colonna a IX_Visit_Id (l'id della colonna VisitItem); molto inverosimile, ma provato comunque. Nessun cambiamento
  • Modificato il livello di isolamento per tornare a leggere il commit (predefinito nel nostro progetto), i deadlock continuano a verificarsi
  • Livello di isolamento modificato in serializzabile. Si verificano ancora deadlock, ma peggio (grafici diversi). Non voglio davvero farlo, comunque.
  • Prendere un lucchetto da tavolo li fa andare via (ovviamente), ma chi vorrebbe farlo?
  • Prendere un blocco pessimistico dell'applicazione (usando sp_getapplock) funziona, ma è praticamente la stessa cosa del blocco della tabella, non voglio farlo.
  • L'aggiunta del suggerimento READPAST al suggerimento XLOCK non ha fatto alcuna differenza
  • Ho disattivato PageLock sull'indice e PK, nessuna differenza
  • Ho aggiunto il suggerimento ROWLOCK al suggerimento XLOCK, senza alcuna differenza

Qualche nota a margine su NHibernate: il modo in cui viene usato e ho capito che funziona è che memorizza nella cache le istruzioni sql fino a quando non trova davvero necessario eseguirle, a meno che non si chiami flush, cosa che non stiamo cercando di fare. Pertanto, la maggior parte delle istruzioni (ad esempio l'elenco aggregato di VisitItems => Visit.VisitItems caricati pigramente) vengono eseguite solo quando necessario. La maggior parte delle istruzioni di aggiornamento ed eliminazione effettive dalla mia transazione viene eseguita alla fine quando viene eseguita la transazione (come è evidente dalla traccia sql sopra). Non ho davvero alcun controllo sull'ordine di esecuzione; NHibernate decide quando fare cosa. La mia dichiarazione di blocco iniziale è davvero solo una soluzione.

Inoltre, con l'istruzione lock, sto solo leggendo gli elementi in un elenco inutilizzato (non sto cercando di sovrascrivere l'elenco VisitItems sull'oggetto Visit poiché non è così che NHibernate dovrebbe funzionare per quanto posso dire). Quindi, anche se ho letto prima l'elenco con l'istruzione personalizzata, NHibernate caricherà nuovamente l'elenco nella sua raccolta di oggetti proxy Visit.VisitItems utilizzando una chiamata sql separata che posso vedere nella traccia quando è il momento di caricarlo pigramente da qualche parte.

Ma questo non dovrebbe importare, giusto? Ho già il lucchetto su detto tasto? Caricarlo di nuovo non lo cambierà?

Come nota finale, forse per chiarire: ogni processo aggiunge prima la propria Visita con VisitItems, quindi entra e lo modifica (che attiverà la cancellazione, l'inserimento e il deadlock). Nei miei test, non c'è mai stato alcun processo che modifica esattamente lo stesso Visit o VisitItems.

Qualcuno ha un'idea su come affrontarlo ulteriormente? Qualcosa che posso provare a aggirare in questo modo in modo intelligente (senza blocchi di tabella ecc.)? Inoltre, vorrei sapere perché questo blocco tripple-x è persino possibile sullo stesso oggetto. Non capisco.

Per favore fatemi sapere se sono necessarie ulteriori informazioni per risolvere il puzzle.

[EDIT] Ho aggiornato la domanda con il DDL per le due tabelle coinvolte.

Inoltre mi è stato chiesto un chiarimento sull'aspettativa: sì, alcuni deadlock qui e ci sono ok, riproveremo o faremo in modo che l'utente rispedisca (in generale). Ma alla frequenza attuale con 12 utenti simultanei, mi aspetto che ce ne sia solo uno ogni poche ore al massimo. Attualmente compaiono più volte al minuto.

Oltre a ciò, ho ottenuto alcune informazioni in più sul trancount = 2, che potrebbe indicare un problema con le transazioni nidificate, che non stiamo realmente utilizzando. Analizzerò anche quello e documenterò i risultati qui.


2
Non utilizzare SELEZIONA *. Potrebbe essere un fattore che contribuisce ai tuoi problemi. Vedere stackoverflow.com/questions/3639861/...
JamieSee

Inoltre, esegui SELECT OBJECT_NAME(objectid, dbid) AS objectname, * FROM sys.dm_exec_sql_text(0x0200000024a9e43033ef90bb631938f939038627209baafb0000000000000000000000000000000000000000)lo sqlhandle su ogni frame di esecuzioneStack per determinare ulteriormente ciò che viene effettivamente eseguito.
Jamie

Stai correndo in una collisione di hash, forse? dba.stackexchange.com/questions/80088/insert-only-deadlocks/…
Johnboy

Ehi ragazzi, temo di non far più parte di questo progetto: - /, quindi non posso provare i vostri suggerimenti. Tuttavia, ho inoltrato il thread e tutte le informazioni ad alcuni membri del team in modo che possano esaminarlo al posto mio.
Ben

È possibile utilizzare la risposta dello script di PowerShell a questa domanda per ottenere ulteriori dettagli di deadlock che potrebbero essere utili. In particolare, recupererà le informazioni sull'istruzione SQL per i frame dello stack "sconosciuti". dba.stackexchange.com/questions/28996/…
JamieVedi

Risposte:


2

Ho fatto un paio di commenti in questo senso, ma non sono sicuro che stai ottenendo i risultati desiderati quando combini il livello di isolamento della transazione di lettura ripetibile con l'istantanea di commit commessa.

Il TIL riportato nell'elenco dei deadlock è una lettura ripetibile, che è ancora più restrittiva di Read Committed, e dato il flusso che descrivi, probabilmente sta portando a deadlock.

Quello che potresti provare a fare è far sì che il tuo DB TIL rimanga leggibile, ma imposti la transazione in modo che usi esplicitamente lo snapshot TIL con un'istruzione di livello di isolamento della transazione impostata. Riferimento: https://msdn.microsoft.com/en-us/library/ms173763.aspx In tal caso, penso che tu debba avere qualcosa di errato. Non ho familiarità con nHibernate, ma sembra che ci sia un riferimento qui: http://www.anujvarma.com/fluent-nhibernate-setting-database-transaction-isolation-level/

Se l'architettura della tua app lo consentirà, un'opzione potrebbe essere quella di provare a leggere l'istantanea impegnata a livello di db e, se ottieni ancora deadlock, abilitare l'istantanea con il controllo delle versioni delle righe. NOTA: in tal caso, è necessario ripensare la configurazione di tempdb se si abilita lo snapshot (versioning delle righe). Posso procurarti ogni sorta di materiale su questo se ne hai bisogno - fammi sapere.


2

Ho un paio di pensieri. Prima di tutto, il modo più semplice per evitare deadlock è quello di prendere sempre i lock nello stesso ordine. Ciò significa che codice diverso che utilizza transazioni esplicite dovrebbe accedere agli oggetti nello stesso ordine, ma anche l'accesso individuale alle righe tramite chiave in una transazione esplicita deve essere ordinato su quella chiave. Prova a ordinare in Visit.VisitItemsbase al PK prima di farlo Addo, a Deletemeno che questa non sia un'enorme raccolta, nel qual caso farei una scelta SELECT.

L'ordinamento probabilmente non è qui il tuo problema. Sto indovinando 2 thread a catturare blocchi condivisi su tutti VisitItemIDi messaggi per un dato VisitIDe i thread A DELETEnon possono essere completati fino a quando il thread B rilascia il suo blocco condiviso che non lo farà fino a quando non viene DELETEcompletato. I blocchi delle app funzioneranno qui e non sono così male come i blocchi delle tabelle poiché si bloccano solo con il metodo e gli altri SELECTfunzioneranno bene. Potresti anche prendere un blocco esclusivo sul Visittavolo per il dato VisitIDma, di nuovo, è potenzialmente eccessivo.

Ti consiglio di trasformare la tua eliminazione definitiva in eliminazione temporanea ( UPDATE ... SET IsDeleted = 1anziché utilizzare DELETE) e di pulire questi record in seguito, in blocco, utilizzando un processo di pulizia che non utilizza transazioni esplicite. Ciò richiederà ovviamente il refactoring di altro codice per ignorare queste righe eliminate, ma è il mio metodo preferito per la gestione di messaggi DELETEinclusi in una SELECTtransazione esplicita.

È inoltre possibile rimuovere il SELECTdalla transazione e passare a un modello di concorrenza ottimista. Entity framework lo fa gratuitamente, non sono sicuro di NHibernate. EF solleverebbe un'eccezione di concorrenza ottimistica se i tuoi DELETEresi fossero 0 righe interessati.


1

Hai provato a spostare l'aggiornamento Visite prima di qualsiasi modifica a visitItems? Quel blocco x dovrebbe proteggere le file "figlio".

Fare una traccia acquisita di blocchi completi (e conversione in lettura leggibile) richiede molto lavoro, ma potrebbe mostrare la sequenza in modo più chiaro.



-1

READ COMMITTED SNAPSHOT ON significa che ogni singola transazione eseguita in READ COMMITTED ISOLATION LEVEL agirà come READ COMMITTED SNAPSHOT.

Ciò significa che i lettori non bloccheranno gli scrittori e gli scrittori non bloccheranno i lettori.

Si utilizza il livello di isolamento delle transazioni di lettura ripetibile, per questo si ha deadlock. Read Committed (senza snapshot) contiene i blocchi sulle righe / pagine fino alla fine dell'istruzione , ma la lettura ripetibile mantiene i blocchi fino alla fine della transazione .

Se dai un'occhiata al tuo grafico Deadlock, puoi vedere un lucchetto a "S" acquisito. Penso che questo sia il blocco per il secondo punto -> "Leggi tutti gli elementi di visita per una determinata visita di VisitId."

  1. Modificare il livello di isolamento della transazione delle connessioni NHibernate in Lettura impegnata
  2. Devi analizzare la query per il tuo secondo punto e capire perché acquisisce blocchi sul PK se hai un indice sulla colonna visitID (potrebbe essere a causa della mancanza di colonne incluse nel tuo indice).
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.