SQL Deadlock บนคีย์คลัสเตอร์ที่ถูกล็อคแบบพิเศษ (พร้อม NHibernate) ที่ลบ / แทรก


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 (โดยใช้ Repeatable Read ด้วย READ_COMMITTED_SNAPSHOT เปิด)
  2. อ่านทั้งหมดรายการเข้าชมสำหรับการเยี่ยมชมที่กำหนดโดยvisitId
  3. รหัสจะประเมินว่ารายการนั้นยังคงมีความเกี่ยวข้องหรือหากเราต้องการกฎใหม่ที่ซับซ้อนโดยใช้กฎที่ซับซ้อน
  4. รหัสจำเป็นต้องเพิ่ม 1 รายการเพิ่มโดยใช้ NHibernate Visit.VisitItems.Add (.. )
  5. รหัสระบุว่าจะต้องลบรายการหนึ่งรายการ (ไม่ใช่รายการที่เราเพิ่งเพิ่ม) ลบออกโดยใช้ NHibernate Visit.VisitItems.Remove (รายการ)
  6. รหัสกระทำการทำธุรกรรม

ด้วยเครื่องมือฉันจำลองคำขอที่เกิดขึ้นพร้อมกัน 12 คำขอซึ่งมีแนวโน้มว่าจะเกิดขึ้นในสภาพแวดล้อมการผลิตในอนาคต

[แก้ไข]ตามคำขอลบรายละเอียดการสอบสวนจำนวนมากที่ฉันเพิ่มไว้ที่นี่เพื่อให้สั้น

หลังจากการวิจัยจำนวนมากขั้นตอนต่อไปคือคิดถึงวิธีที่ฉันสามารถล็อคคำใบ้บนดัชนีที่แตกต่างจากที่ใช้ในส่วนคำสั่ง (เช่นคีย์หลักเนื่องจากใช้สำหรับการลบ) ดังนั้นฉันจึงเปลี่ยนคำสั่งล็อคของฉันเป็น :

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

สิ่งนี้ลดการหยุดชะงักในความถี่เล็กน้อย แต่ยังคงเกิดขึ้น และนี่คือที่ฉันเริ่มหลงทาง:

สามล็อคพิเศษ?

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

การสืบค้นจำนวนคิวรีที่เป็นผลลัพธ์จะมีลักษณะเช่นนี้
[แก้ไข]โอ้โห สัปดาห์ละ ตอนนี้ฉันได้อัพเดตการติดตามด้วยการติดตามที่ไม่มีการตอบรับของคำสั่งที่เกี่ยวข้องซึ่งฉันคิดว่านำไปสู่การหยุดชะงัก

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

ตอนนี้ล็อคของฉันดูเหมือนจะมีผลกระทบเพราะมันแสดงในกราฟการหยุดชะงัก แต่อะไร? สามล็อคพิเศษและล็อคหนึ่งที่ใช้ร่วมกัน? มันทำงานบนวัตถุ / คีย์เดียวกันได้อย่างไร ฉันคิดว่าตราบใดที่คุณมีการล็อคแบบเอกสิทธิ์เฉพาะบุคคลคุณจะไม่สามารถล็อคการแชร์จากคนอื่นได้หรือ และวิธีอื่น ๆ หากคุณมีการล็อคที่ใช้ร่วมกันไม่มีใครสามารถได้รับการล็อคพิเศษพวกเขาต้องรอ

ฉันคิดว่าฉันขาดความเข้าใจที่ลึกลงไปที่นี่เกี่ยวกับวิธีการล็อคทำงานเมื่อมีการใช้ปุ่มหลาย ๆ ตัวในตารางเดียวกัน

นี่คือบางสิ่งที่ฉันได้ลองและผลกระทบของมัน:

  • เพิ่มคำใบ้ดัชนีอื่นใน IX_Visit_Id ไปยังคำสั่ง lock ไม่มีการเปลี่ยนแปลง
  • เพิ่มคอลัมน์ที่สองใน IX_Visit_Id (รหัสของ VisitItem คอลัมน์); ดึงข้อมูลได้ไกล แต่พยายามต่อไป ไม่มีการเปลี่ยนแปลง
  • เปลี่ยนระดับการแยกกลับไปที่อ่านความมุ่งมั่น (ค่าเริ่มต้นในโครงการของเรา) การหยุดชะงักยังคงเกิดขึ้น
  • เปลี่ยนระดับการแยกเป็นซีเรียลไลซ์ การหยุดชะงักยังคงเกิดขึ้น แต่แย่ลง (กราฟที่แตกต่างกัน) ฉันไม่ต้องการทำเช่นนั้น
  • การล็อคตารางทำให้พวกมันหายไป (ชัด) แต่ใครจะทำล่ะ
  • การล็อคแอปพลิเคชันในแง่ร้าย (โดยใช้ sp_getapplock) ใช้งานได้ แต่นั่นก็เป็นสิ่งเดียวกับล็อคตารางไม่ต้องการทำเช่นนั้น
  • การเพิ่มคำแนะนำ READPAST ให้กับคำใบ้ XLOCK นั้นไม่ต่างกัน
  • ฉันปิด PageLock สำหรับดัชนีและ PK ไม่แตกต่างกัน
  • ฉันได้เพิ่มคำใบ้ ROWLOCK ไปยังคำแนะนำ XLOCK แล้วไม่แตกต่างกัน

ข้อสังเกตบางประการเกี่ยวกับ NHibernate: วิธีการใช้งานและฉันเข้าใจว่ามันใช้งานได้คือมันเก็บคำสั่ง sql จนกว่าจะพบว่าจำเป็นต้องเรียกใช้งานจริงเว้นแต่คุณจะเรียกใช้ flush ซึ่งเราพยายามจะไม่ทำ ดังนั้นข้อความส่วนใหญ่ (เช่นรายการสรุปรวมของ VisitItems => Visit.VisitItems) ที่โหลดอย่างเกียจคร้าน) จะถูกดำเนินการเมื่อจำเป็นเท่านั้น ส่วนใหญ่ของการปรับปรุงจริงและลบคำสั่งจากการทำธุรกรรมของฉันได้รับการดำเนินการในตอนท้ายเมื่อการทำธุรกรรมมีความมุ่งมั่น (ตามที่เห็นได้ชัดจากการติดตาม sql ข้างต้น) ฉันไม่สามารถควบคุมลำดับการดำเนินการได้จริงๆ NHibernate ตัดสินใจเมื่อจะทำอะไร คำสั่งล็อคเริ่มต้นของฉันเป็นเพียงการแก้ไข

นอกจากนี้ด้วยคำสั่ง lock ฉันเพิ่งอ่านรายการลงในรายการที่ไม่ได้ใช้ (ฉันไม่ได้พยายามแทนที่รายการ VisitItems บนวัตถุ Visit เนื่องจากไม่ใช่วิธีที่ NHibernate ควรทำงานเท่าที่ฉันจะบอกได้) ดังนั้นแม้ว่าฉันจะอ่านรายการแรกด้วยคำสั่งที่กำหนดเอง NHibernate จะยังคงโหลดรายการอีกครั้งในคอลเลกชันวัตถุของพร็อกซี Visit.VisitItems โดยใช้การเรียก sql แยกต่างหากที่ฉันเห็นในการติดตามเมื่อถึงเวลาโหลดอย่างเหงา

แต่นั่นไม่ควรสำคัญใช่ไหม ฉันมีกุญแจที่ล็อคอยู่แล้ว? การโหลดอีกครั้งจะไม่เปลี่ยนแปลงหรือไม่

ในฐานะโน้ตสุดท้ายอาจจะอธิบายให้ชัดเจน: แต่ละกระบวนการเพิ่มการเข้าชมของตัวเองด้วย VisitItems ก่อนจากนั้นจึงเข้าไปและแก้ไข (ซึ่งจะทำให้การลบและการแทรกและการหยุดชะงัก) ในการทดสอบของฉันไม่มีกระบวนการใดเปลี่ยนแปลงการเข้าชมหรือ VisitItems เดียวกัน

ใครบ้างมีความคิดเกี่ยวกับวิธีการนี้อีกต่อไป? มีอะไรที่ฉันสามารถลองแก้ไขได้ด้วยวิธีที่ชาญฉลาด (ไม่มีระบบล็อคโต๊ะ ฯลฯ ) นอกจากนี้ฉันต้องการที่จะเรียนรู้ว่าเหตุใดการล็อค tripple-x นี้จึงเป็นไปได้ในวัตถุเดียวกัน ฉันไม่เข้าใจ

โปรดแจ้งให้เราทราบหากต้องการข้อมูลเพิ่มเติมเพื่อแก้ปริศนา

[แก้ไข] ฉันอัพเดตคำถามด้วย DDL สำหรับสองตารางที่เกี่ยวข้อง

นอกจากนี้ฉันถูกขอให้ชี้แจงเกี่ยวกับความคาดหวัง: ใช่มี deadlock สองสามตัวที่นี่และก็โอเคเราจะลองใหม่หรือให้ผู้ใช้ส่งอีกครั้ง (โดยทั่วไปจะพูด) แต่ด้วยความถี่ปัจจุบันที่มีผู้ใช้งานพร้อมกัน 12 คนฉันคาดหวังว่าจะมีเพียงหนึ่งครั้งต่อชั่วโมง ขณะนี้พวกเขาปรากฏขึ้นหลายครั้งต่อนาที

นอกจากนั้นฉันได้รับข้อมูลเพิ่มเติมเกี่ยวกับ trancount = 2 ซึ่งอาจบ่งบอกถึงปัญหาของการทำธุรกรรมที่ซ้อนกันซึ่งเราไม่ได้ใช้ ฉันจะตรวจสอบด้วยและจัดทำเอกสารผลลัพธ์ที่นี่


2
ห้ามใช้ SELECT * อาจเป็นปัจจัยสนับสนุนปัญหาของคุณ ดูstackoverflow.com/questions/3639861/…
JamieSee

นอกจากนี้ให้เรียกใช้SELECT OBJECT_NAME(objectid, dbid) AS objectname, * FROM sys.dm_exec_sql_text(0x0200000024a9e43033ef90bb631938f939038627209baafb0000000000000000000000000000000000000000)sqlhandle ในแต่ละ executionStack frame เพื่อกำหนดว่าอะไรที่จะถูกดำเนินการจริง
JamieSee

คุณกำลังชนเข้ากับแฮ็ชหรือเปล่า? dba.stackexchange.com/questions/80088/insert-only-deadlocks/…
Johnboy

เฮ้พวกฉันกลัวว่าฉันไม่ได้เป็นส่วนหนึ่งของโครงการนี้อีกแล้ว: - / ดังนั้นฉันจึงไม่สามารถลองทำตามคำแนะนำของคุณได้ อย่างไรก็ตามฉันได้ส่งต่อเธรดและข้อมูลทั้งหมดไปยังสมาชิกในทีมบางคนเพื่อให้พวกเขาสามารถดูได้ในตำแหน่งของฉัน
Ben

คุณสามารถใช้คำตอบของสคริปต์ PowerShell กับคำถามนี้เพื่อรับรายละเอียดการหยุดชะงักเพิ่มเติมที่อาจช่วยคุณได้ มันจะดึงข้อมูลคำสั่ง SQL สำหรับเฟรมสแต็ก "ไม่ทราบ" ของคุณ dba.stackexchange.com/questions/28996/…
JamieSee

คำตอบ:


2

ฉันแสดงความคิดเห็นเกี่ยวกับเอฟเฟกต์นี้ แต่ฉันไม่แน่ใจว่าคุณจะได้รับผลลัพธ์ที่ต้องการเมื่อคุณรวมระดับการแยกธุรกรรมแบบอ่านซ้ำในการทำธุรกรรมเข้ากับ Read Commaps Snapshot

TIL ที่รายงานในรายการการหยุดชะงักของคุณนั้นสามารถอ่านซ้ำได้ซึ่งมีข้อ จำกัด มากกว่า Read Committed และเมื่อมีกระแสที่คุณอธิบายมีแนวโน้มที่จะนำไปสู่การหยุดชะงัก

สิ่งที่คุณอาจพยายามทำคือให้ DB TIL ของคุณยังคงอ่านซ้ำได้ แต่ตั้งค่าธุรกรรมให้ใช้ snapshot TIL อย่างชัดเจนด้วยชุดคำสั่งระดับธุรกรรมแยก การอ้างอิง: https://msdn.microsoft.com/en-us/library/ms173763.aspx ถ้าใช่ฉันคิดว่าคุณต้องมีบางอย่างที่ไม่ถูกต้อง ฉันไม่คุ้นเคยกับ nHibernate แต่ดูเหมือนว่ามีการอ้างอิงที่นี่: http://www.anujvarma.com/fluent-nhibernate-setting-database-transaction-isolation-level/

หากสถาปัตยกรรมของแอปของคุณอนุญาตให้ใช้ตัวเลือกหนึ่งคือลองอ่านสแน็ปช็อตที่กำหนดที่ระดับ db และหากคุณยังคงได้รับ deadlocks ให้เปิดใช้งานสแน็ปช็อตด้วยการกำหนดเวอร์ชันแถว โปรดทราบว่าหากคุณทำเช่นนี้คุณต้องคิดใหม่การตั้งค่า tempdb ของคุณหากคุณเปิดใช้งานสแนปชอต (การกำหนดเวอร์ชันแถว) ฉันสามารถให้คุณได้รับวัสดุทุกประเภทหากคุณต้องการ - แจ้งให้เราทราบ


2

ฉันมีความคิดสองสามอย่าง ก่อนอื่นวิธีที่ง่ายที่สุดในการหลีกเลี่ยงการหยุดชะงักคือการล็อคในลำดับเดียวกันเสมอ นั่นหมายความว่ารหัสที่แตกต่างกันโดยใช้การทำธุรกรรมที่ชัดเจนควรเข้าถึงวัตถุในลำดับเดียวกัน แต่ยังเข้าถึงแถวทีละรายการโดยคีย์ในธุรกรรมที่ชัดเจนควรจะเรียงลำดับในคีย์นั้น ลองเรียงลำดับVisit.VisitItemsโดย PK ของมันก่อนที่จะทำAddหรือDeleteเว้นแต่จะเป็นชุดใหญ่ที่ฉันเรียงลำดับSELECTเว้นแต่นี้เป็นชุดใหญ่ซึ่งในกรณีนี้ผมจะเรียงลำดับใน

การเรียงลำดับอาจไม่ใช่ปัญหาของคุณที่นี่ ฉันคาดเดา 2 หัวข้อคว้าล็อคในทุกที่ใช้ร่วมกันVisitItemIDสำหรับการที่กำหนดVisitIDและด้ายของ A DELETEไม่สามารถสมบูรณ์จนกว่าด้าย B ออกล็อคที่ใช้ร่วมกันซึ่งมันจะไม่จนDELETEเสร็จสมบูรณ์ การล็อคแอปจะทำงานที่นี่และไม่เลวเท่าการล็อคตารางเนื่องจากจะบล็อกโดยวิธีการเท่านั้นและการล็อคแอปอื่น ๆSELECTก็ใช้งานได้ดี คุณสามารถล็อคพิเศษบนVisitโต๊ะสำหรับที่กำหนดVisitIDแต่อีกครั้งนั่นอาจเป็นทักษะมากเกินไป

ฉันขอแนะนำให้เปลี่ยนการลบฮาร์ดของคุณเป็นการลบแบบอ่อน ๆ ( UPDATE ... SET IsDeleted = 1แทนที่จะใช้DELETE) และล้างข้อมูลเหล่านี้ในภายหลังเป็นกลุ่มโดยใช้งานการล้างข้อมูลที่ไม่ได้ใช้ธุรกรรมที่ชัดเจน สิ่งนี้จะต้องมีการเปลี่ยนรหัสอีกครั้งเพื่อเพิกเฉยต่อแถวที่ถูกลบเหล่านี้ แต่เป็นวิธีที่ฉันต้องการสำหรับจัดการกับการDELETEรวมSELECTในการทำธุรกรรมอย่างชัดเจน

นอกจากนี้คุณยังสามารถลบออกSELECTจากการทำธุรกรรมและเปลี่ยนเป็นรูปแบบการทำงานพร้อมกันในแง่ดี กรอบเอนทิตีทำสิ่งนี้ได้ฟรีไม่แน่ใจเกี่ยวกับ NHibernate EF จะเพิ่มข้อยกเว้นการเห็นพ้องในแง่ดีหากDELETEผลตอบแทนของคุณ0 แถว


1

คุณได้ลองย้ายอัปเดตการเข้าชมก่อนที่จะแก้ไขใด ๆ เพื่อเข้าชมรายการหรือไม่ x-lock นั้นควรป้องกันแถว "ลูก"

การทำการล็อคแบบเต็มที่ได้รับการติดตาม (และการแปลงเป็นแบบมนุษย์สามารถอ่านได้) เป็นงานจำนวนมาก แต่อาจแสดงลำดับได้ชัดเจนยิ่งขึ้น


-1

หากคุณไม่มีเงื่อนงำว่าทำไมตารางกำลังติดอยู่บางครั้งก็มีทรานติดอยู่

SET XACT_ABORT ON -> สิ่งนี้ควรดูแลข้อผิดพลาดที่ทำให้ tran ติดอยู่ BEGIN TRAN TRAN_NAME - โค๊ดเข้าถึงตาราง - COMMIT TRAN TRAN_NAME

https://stackoverflow.com/questions/2277254/how-to-set-xact-abort-within-ado-net


-1

อ่าน SNAPSHOT ที่เปิดใช้แล้วหมายความว่าทุกธุรกรรมเดียวที่ทำงานในระดับ ISOLATION IST ที่ได้รับการยอมรับจะทำหน้าที่เป็น SNAPSHOT ที่ได้รับคำสั่งแล้ว

ซึ่งหมายความว่าผู้อ่านจะไม่บล็อกนักเขียนและนักเขียนจะไม่บล็อกผู้อ่าน

คุณใช้ระดับการแยกธุรกรรมอ่านซ้ำได้นี่คือสาเหตุที่คุณมีการหยุดชะงัก Read Committed (ไม่มีสแนปช็อต) ถือการล็อกในแถว / หน้าจนกว่าจะสิ้นสุดคำสั่งแต่การอ่านซ้ำจะระงับการล็อกจนกว่าจะสิ้นสุดการทำธุรกรรมจุดสิ้นสุดของการทำธุรกรรม

หากคุณดูกราฟเดดล็อกของคุณคุณจะเห็นล็อค "S" ที่ได้รับ ฉันคิดว่านี่เป็นล็อคตามจุดที่สอง -> "อ่านรายการเยี่ยมชมทั้งหมดสำหรับการเข้าชมที่กำหนดโดย VisitId"

  1. เปลี่ยนระดับการแยกธุรกรรมการเชื่อมต่อ NHibernate ของคุณเป็นอ่านแล้ว
  2. คุณต้องวิเคราะห์แบบสอบถามสำหรับจุดที่ 2 ของคุณและเข้าใจว่าเหตุใดจึงได้รับการล็อกบน PK หากคุณมีดัชนีในคอลัมน์ visitID ของคุณ (อาจเป็นเพราะขาดคอลัมน์ที่รวมอยู่ในดัชนีของคุณ)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.