삭제 / 삽입시 동일한 독점 잠금 클러스터 키 (NHibernate 포함)의 SQL 교착 상태


29

나는이 교착 상태 문제를 지금 며칠 동안 일해 왔으며 내가하는 일에 관계없이 어떤 방식 으로든 지속됩니다.

첫째, 일반적인 전제 : 우리는 일대 다 관계로 VisitItems를 방문합니다.

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)

방문 정보 :

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

여러 사용자가 다음과 같은 방법으로 VisitItems 테이블을 동시에 업데이트하려고합니다.

별도의 웹 요청은 VisitItems (일반적으로 1)로 방문을 만듭니다. 그런 다음 (문제 요청) :

  1. 웹 요청이 들어 와서 NHibernate 세션을 열고 NHibernate 트랜잭션을 시작합니다 (READ_COMMITTED_SNAPSHOT이 설정된 반복 읽기 사용).
  2. VisitId 가 특정 방문에 대한 모든 방문 항목을 읽습니다 .
  3. 코드는 항목이 여전히 관련성이 있는지 또는 복잡한 규칙을 사용하여 새로운 항목이 필요한지 평가합니다 (예 : 40ms와 같이 약간 길어짐).
  4. 코드에서 1 개의 항목을 추가해야하고 NHibernate Visit.VisitItems.Add (..)를 사용하여 추가해야합니다.
  5. 코드는 방금 추가 한 항목이 아닌 하나의 항목을 삭제해야한다는 것을 식별하고 NHibernate Visit.VisitItems.Remove (item)를 사용하여 제거합니다.
  6. 코드가 트랜잭션을 커밋

도구를 사용하여 향후 프로덕션 환경에서 발생할 가능성이 높은 12 개의 동시 요청을 시뮬레이션합니다.

[편집] 요청에 따라 여기에 추가 한 조사 세부 정보를 많이 삭제했습니다.

많은 연구를 한 후 다음 단계는 where 절에 사용 된 것과 다른 색인에 힌트를 잠글 수있는 방법을 생각하는 것이 었습니다 (즉, 기본 키는 삭제에 사용되었으므로). :

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

이로 인해 교착 상태가 약간 줄어들었지만 여전히 발생하고 있습니다. 그리고 여기 내가 길을 잃기 시작하는 곳이 있습니다.

3 개의 독점 잠금 장치?

<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>

결과 쿼리 수의 추적은 다음과 같습니다.
[EDIT] 워. 일주일 이요 교착 상태로 이어질 것으로 생각되는 관련 문장의 수정되지 않은 흔적으로 추적을 업데이트했습니다.

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

이제 내 잠금은 교착 상태 그래프에 표시되므로 효과가있는 것 같습니다. 근데 뭐? 3 개의 독점 잠금 및 1 개의 공유 잠금? 동일한 객체 / 키에서 어떻게 작동합니까? 독점 잠금이있는 한 다른 사람으로부터 공유 잠금을 얻을 수 없다고 생각 했습니까? 그리고 다른 방법. 공유 잠금이 있으면 아무도 독점 잠금을 얻을 수 없으며 대기해야합니다.

동일한 테이블의 여러 키에서 잠금을 사용할 때 잠금이 작동하는 방식에 대한 이해가 부족하다고 생각합니다.

내가 시도한 것들과 그 영향은 다음과 같습니다.

  • IX_Visit_Id에 대한 또 다른 색인 힌트를 잠금 문에 추가했습니다. 변경 없음
  • IX_Visit_Id (방문 항목 열의 ID)에 두 번째 열을 추가했습니다. 멀리 가져 왔지만 어쨌든 시도했습니다. 변경 없음
  • 격리 수준을 커밋 된 읽기 (프로젝트의 기본값)로 다시 변경했는데 교착 상태가 여전히 발생 함
  • 격리 수준을 직렬화 가능으로 변경했습니다. 교착 상태는 여전히 발생하지만 더 나쁩니다 (다른 그래프). 어쨌든 나는 그렇게하고 싶지 않습니다.
  • 테이블 잠금 장치를 사용하면 테이블이 사라지지만 누가 그렇게하겠습니까?
  • sp_getapplock을 사용하여 비관적 응용 프로그램 잠금을 사용하면 작동하지만 테이블 잠금과 거의 동일합니다.
  • XLOCK 힌트에 READPAST 힌트를 추가해도 아무런 차이가 없습니다.
  • 색인 및 PK에서 PageLock을 해제했습니다. 차이가 없습니다.
  • XLOCK 힌트에 ROWLOCK 힌트를 추가했지만 아무런 차이가 없었습니다.

NHibernate에 대한 몇 가지 참고 사항 : 사용 된 방법과 작동 방식은 플러시를 호출하지 않는 한 실제로 실행해야 할 때까지 sql 문을 캐시한다는 것입니다. 따라서 대부분의 명령문 (예 : 느리게로드 된 VisitItems => Visit.VisitItems의 집계 목록)은 필요할 때만 실행됩니다. 내 트랜잭션의 실제 업데이트 및 삭제 문 대부분은 트랜잭션이 커밋되면 끝납니다 (위의 SQL 추적에서 알 수 있음). 나는 실제로 실행 순서를 통제 할 수 없다. NHibernate는 언제 무엇을해야할지 결정합니다. 내 초기 잠금 문은 실제로 해결 방법입니다.

또한 lock 문을 사용하여 항목을 사용하지 않는 목록으로 읽습니다 (방문객 개체의 VisitItems 목록을 재정의하려고하지 않습니다. 왜냐하면 NHibernate가 내가 알 수있는 한 작동하지 않기 때문입니다). 따라서 사용자 지정 문으로 목록을 먼저 읽었더라도 NHibernate는 여전히 어딘가에로드 할 때 추적에서 볼 수있는 별도의 SQL 호출을 사용하여 프록시 객체 컬렉션 Visit.VisitItems에 다시 목록을로드합니다.

그러나 그것은 중요하지 않습니다. 이미 열쇠에 자물쇠가 있습니까? 다시로드해도 변경되지 않습니까?

마지막으로, 명확히 할 수 있습니다 : 각 프로세스는 먼저 VisitItems를 사용하여 자체 방문을 추가 한 다음 수정하고 삭제합니다 (삭제 및 삽입 및 교착 상태를 유발합니다). 내 테스트에서 정확히 동일한 방문 또는 방문 항목을 변경하는 프로세스는 없습니다.

아무도 이것에 더 접근하는 방법에 대한 아이디어가 있습니까? 현명한 방법으로 (테이블 잠금 없음)이 문제를 해결할 수 있습니까? 또한이 tripple-x 잠금이 동일한 객체에서 가능한 이유를 배우고 싶습니다. 이해가 안 돼요

퍼즐을 풀기 위해 더 많은 정보가 필요하면 알려주십시오.

[편집] 관련된 두 테이블에 대해 DDL로 질문을 업데이트했습니다.

또한 나는 기대에 대한 설명을 요구 받았다. 네, 여기에 몇 가지 교착 상태가 있고 괜찮습니다. 다시 시도하거나 사용자가 다시 제출하도록하십시오 (일반적으로 말하면). 그러나 12 명의 동시 사용자가있는 현재 빈도에서는 최대 몇 시간마다 한 명만있을 것으로 예상합니다. 현재는 분당 여러 번 나타납니다.

또한 trancount = 2에 대한 정보가 더 있는데, 실제로 사용하지 않는 중첩 트랜잭션에 문제가 있음을 나타냅니다. 나는 그것을 조사하고 여기에 결과를 문서화 할 것이다.


2
SELECT *를 사용하지 마십시오. 문제의 원인이 될 수 있습니다. stackoverflow.com/questions/3639861/…
Jamie

또한 SELECT OBJECT_NAME(objectid, dbid) AS objectname, * FROM sys.dm_exec_sql_text(0x0200000024a9e43033ef90bb631938f939038627209baafb0000000000000000000000000000000000000000)각 executionStack 프레임에서 sqlhandle에 대해 실행하여 실제로 실행중인 항목을 추가로 판별하십시오.
JamieSee

아마도 해시 충돌을 겪고 있습니까? dba.stackexchange.com/questions/80088/insert-only-deadlocks/…
Johnboy

안녕하세요, 저는 더 이상이 프로젝트에 참여하지 않을 것입니다 :-/, 그래서 귀하의 제안을 시도 할 수 없습니다. 그러나 스레드와 모든 정보를 일부 팀 구성원에게 전달하여 내 대신 볼 수 있도록했습니다.
Ben

이 질문에 대한 PowerShell 스크립트 답변을 사용하면 도움이 될 수있는 교착 상태 세부 정보를 얻을 수 있습니다. 특히 "알 수없는"스택 프레임에 대한 SQL 문 정보를 검색합니다. dba.stackexchange.com/questions/28996/…
Jamie

답변:


2

이 효과에 대해 몇 가지 의견을 제시했지만 반복 가능한 읽기 트랜잭션 격리 수준과 읽기 커밋 된 스냅 샷을 결합 할 때 원하는 결과를 얻지 못할 수 있습니다.

교착 상태 목록에보고 된 TIL은 반복 가능한 읽기로, 읽기 커밋보다 훨씬 제한적이며 설명 된 흐름에 따라 교착 상태가 발생할 수 있습니다.

수행하려는 작업은 DB TIL을 반복적으로 읽을 수 있도록 설정하지만 트랜잭션 격리 수준 명령문을 설정하여 스냅 샷 TIL을 명시 적으로 사용하도록 트랜잭션을 설정하는 것입니다. 참조 : https://msdn.microsoft.com/en-us/library/ms173763.aspx 그렇다면 잘못된 것이 있다고 생각합니다. nHibernate에 익숙하지 않지만 여기에 참조가있는 것 같습니다 : http://www.anujvarma.com/fluent-nhibernate-setting-database-transaction-isolation-level/

앱 아키텍처에서 허용하는 경우 옵션은 커밋 된 스냅 샷을 db 수준에서 시도하고 여전히 교착 상태가 발생하면 행 버전 관리로 스냅 샷을 활성화하는 것입니다. 이 작업을 수행하는 경우 스냅 샷 (행 버전 관리)을 활성화하면 tempdb 설정을 다시 생각해야합니다. 필요한 경우 모든 종류의 자료를 얻을 수 있습니다. 알려주십시오.


2

몇 가지 생각이 있습니다. 우선 교착 상태를 피하는 가장 쉬운 방법은 항상 같은 순서로 잠금을 수행하는 것입니다. 즉, 명시 적 트랜잭션을 사용하는 다른 코드는 동일한 순서로 객체에 액세스해야하지만 명시 적 트랜잭션의 키를 사용하여 개별적으로 행에 액세스하는 것도 해당 키에서 정렬되어야합니다. Visit.VisitItems수행하기 전에 Add또는 Delete내가 정렬 할 거대한 컬렉션이 아닌 한 PK 별로 정렬 을 시도하십시오 SELECT.

정렬은 아마 여기서 문제가되지 않습니다. 나는 2 개 스레드가 모든에 공유 잠금 잡아 추측하고있어 VisitItemID주어진에 대한들 VisitID과의 스레드 DELETE스레드 B가 공유 잠금을 해제 할 때까지 완료 할 수 없습니다를하는 것이하지 않습니다 그것까지 DELETE완료. 앱 잠금은 여기에서 작동하며 테이블 잠금만큼 나쁘지 않습니다. 방법으로 만 차단하고 다른 잠금 SELECT은 제대로 작동하기 때문입니다. 당신은 또한 Visit주어진 테이블에 대해 독점적 인 잠금을 취할 수 VisitID있지만 다시 말하지만, 그것은 잠재적으로 과잉입니다.

하드 삭제를 소프트 삭제 () UPDATE ... SET IsDeleted = 1대신 소프트 삭제로 바꾸고 DELETE나중에 명시 적 트랜잭션을 사용하지 않는 정리 작업을 사용하여 이러한 레코드를 대량으로 정리하는 것이 좋습니다. 이것은 분명히 삭제 된 행을 무시하기 위해 다른 코드를 리팩토링해야하지만 명시 적 트랜잭션에 DELETE포함 된 s 를 처리하기 위해 선호하는 방법입니다 SELECT.

SELECT트랜잭션에서를 제거하고 낙관적 동시성 모델로 전환 할 수도 있습니다. 엔터티 프레임 워크는 NHibernate에 대해 확실하지 않고 무료 로이 작업을 수행합니다. EF DELETE는 0 행이 영향을받는 경우 낙관적 동시성 예외를 발생시킵니다 .


1

visitItems를 수정하기 전에 방문 업데이트를 이동하려고 했습니까? 이 x-lock은 "자식"행을 보호해야합니다.

전체 잠금 획득 추적 (및 사람이 읽을 수있는 변환)을 수행하는 것은 많은 작업이지만 시퀀스를보다 명확하게 보여줄 수 있습니다.



-1

READ COMMITTED SNAPSHOT ON은 READ COMMITTED ISOLATION LEVEL에서 실행되는 모든 단일 트랜잭션이 READ COMMITTED SNAPSHOT으로 작동 함을 의미합니다.

즉, 독자는 작성자를 차단하지 않으며 작성자는 독자를 차단하지 않습니다.

반복 가능한 읽기 트랜잭션 격리 수준을 사용하므로 교착 상태가 발생합니다. 커밋 된 읽기 (스냅 샷없이)는 명령문이 끝날 때까지 행 / 페이지 의 잠금을 유지하지만 반복 가능한 읽기 는 트랜잭션이 끝날 때까지 잠금을 유지합니다 .

교착 상태 그래프를 보면 "S"잠금이 획득 된 것을 볼 수 있습니다. 이것이 두 번째 요점-> "방문한 방문의 모든 방문 항목을 VisitId로 읽습니다."

  1. NHibernate 연결 트랜잭션 격리 레벨을 읽기 커밋으로 변경
  2. visitID 열에 인덱스가있는 경우 두 번째 포인트에 대한 쿼리를 분석하고 PK에서 잠금을 획득하는 이유를 이해해야합니다 (인덱스에 포함 된 열이 없기 때문일 수 있음).
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.