อะไรคือสาเหตุหลักของการหยุดชะงักและสามารถป้องกันได้?


55

เมื่อเร็ว ๆ นี้หนึ่งในแอปพลิเคชัน ASP.NET ของเราแสดงข้อผิดพลาดการหยุดชะงักของฐานข้อมูลและฉันถูกขอให้ตรวจสอบและแก้ไขข้อผิดพลาด ฉันจัดการเพื่อค้นหาสาเหตุของการหยุดชะงักเป็นขั้นตอนการจัดเก็บที่ปรับปรุงตารางภายในเคอร์เซอร์อย่างจริงจัง

นี่เป็นครั้งแรกที่ฉันเห็นข้อผิดพลาดนี้และไม่รู้วิธีการติดตามและแก้ไขอย่างมีประสิทธิภาพ ฉันลองทุกวิธีที่เป็นไปได้ที่ฉันรู้และในที่สุดก็พบว่าตารางที่กำลังอัปเดตไม่มีคีย์หลัก! โชคดีที่มันเป็นคอลัมน์ตัวตน

ฉันภายหลังพบนักพัฒนาที่เขียนสคริปต์ฐานข้อมูลสำหรับการปรับใช้ที่สับสน ฉันเพิ่มคีย์หลักและปัญหาได้รับการแก้ไข

ฉันรู้สึกมีความสุขและกลับมาที่โครงการของฉันและทำการวิจัยเพื่อค้นหาสาเหตุของการหยุดชะงัก ...

เห็นได้ชัดว่ามันเป็นเงื่อนไขการรอแบบวงกลมที่ทำให้เกิดการหยุดชะงัก การอัพเดตใช้เวลานานกว่าโดยไม่มีคีย์หลักมากกว่ากับคีย์หลัก

ฉันรู้ว่ามันไม่ใช่ข้อสรุปที่ชัดเจนนั่นคือเหตุผลที่ฉันโพสต์ที่นี่ ...

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

2
IME ส่วนใหญ่ (ทั้งหมดหรือไม่) ของการหยุดชะงักที่ฉันเคยเห็นเกิดขึ้นเนื่องจากการรอแบบวงกลม
Sathyajith Bhat

การเวียนเป็นหนึ่งในเงื่อนไขที่จำเป็นของการหยุดชะงัก คุณสามารถหลีกเลี่ยงการหยุดชะงักใด ๆ หากเซสชันทั้งหมดของคุณได้รับการล็อคในลำดับเดียวกัน
Peter G.

คำตอบ:


38

การติดตามการหยุดชะงักเป็นเรื่องง่ายกว่าสำหรับสองคนนี้:

โดยค่าเริ่มต้น deadlocks จะไม่ถูกเขียนในบันทึกข้อผิดพลาด คุณสามารถทำให้ SQL เขียน deadlocks ลงในบันทึกข้อผิดพลาดด้วยค่าสถานะการติดตาม 1204 และ 3605

เขียนข้อมูลการหยุดชะงักไปยังบันทึกข้อผิดพลาดของ SQL Server: DBCC TRACEON (-1, 1204, 3605)

ปิด: DBCC TRACEOFF (-1, 1204, 3605)

โปรดดูที่ "การแก้ไขปัญหาการหยุดชะงัก" สำหรับการสนทนาของแฟล็กการติดตาม 1204 และเอาต์พุตที่คุณจะได้รับเมื่อเปิดใช้งาน https://msdn.microsoft.com/en-us/library/ms178104.aspx

การป้องกันเป็นเรื่องยากมากขึ้นโดยพื้นฐานแล้วคุณต้องระวังสิ่งต่อไปนี้:

รหัสบล็อก 1 ล็อคทรัพยากร A จากนั้นทรัพยากร B ในลำดับนั้น

รหัสบล็อก 2 ล็อคทรัพยากร B จากนั้นทรัพยากร A ตามลำดับนั้น

นี่เป็นเงื่อนไขแบบคลาสสิกที่การหยุดชะงักสามารถเกิดขึ้นได้หากการล็อคของทั้งสองทรัพยากรไม่ได้เป็นอะตอมรหัสบล็อก 1 สามารถล็อค A และถูกจองไว้ล่วงหน้าแล้วบล็อกบล็อค 2 ล็อค B ก่อนที่ A จะประมวลผลเวลาย้อนกลับ ตอนนี้คุณมีการหยุดชะงัก

เพื่อป้องกันเงื่อนไขนี้คุณสามารถทำสิ่งต่อไปนี้

Code Block A (รหัส psuedo)

Lock Shared Resource Z
    Lock Resource A
    Lock Resource B
Unlock Shared Resource Z
...

รหัสบล็อก B (รหัสหลอก)

Lock Shared Resource Z
    Lock Resource B
    Lock Resource A
Unlock Shared Resource Z
...

ไม่ลืมที่จะปลดล็อค A และ B เมื่อเสร็จแล้ว

สิ่งนี้จะป้องกันการหยุดชะงักระหว่างรหัสบล็อก A และรหัสบล็อก B

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


คุณหมายถึงการล็อคทรัพยากร A ก่อนทรัพยากร B ใน Code Block B หรือไม่ ตามที่เขียนไว้นี้จะทำให้เกิดการหยุดชะงัก .. ตามที่คุณพูดถึงในความคิดเห็นก่อนหน้านี้ มากที่สุดที่เป็นไปได้คุณต้องการล็อกทรัพยากรในลำดับเดียวกันเสมอแม้ว่าคุณต้องการคิวรีแบบจำลองตั้งแต่ต้นเพื่อให้แน่ใจว่าลำดับการล็อกนั้น
เจอราร์ดโอนีล

23

บทความที่ชื่นชอบการอ่านและการเรียนรู้เกี่ยวกับการติดตายคือ พูดง่าย - ติดตามลง deadlocks และ SQL Server กลาง - การใช้ Profiler เพื่อแก้ไขปัญหาการติดตาย พวกเขาจะให้ตัวอย่างและคำแนะนำเกี่ยวกับวิธีการจัดการกับสถานการณ์ที่แย่

ในระยะสั้นเพื่อแก้ปัญหาในปัจจุบันฉันจะทำธุรกรรมที่เกี่ยวข้องให้สั้นลงนำส่วนที่ไม่จำเป็นออกไปดูแลลำดับการใช้วัตถุดูระดับการแยกที่จำเป็นจริง ๆ อ่านไม่จำเป็น ข้อมูล...

แต่ควรอ่านบทความให้ดียิ่งขึ้นพวกเขาจะได้คำแนะนำที่ดีกว่า


16

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

ตัวอย่างเช่นในInnoDB :

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

วิธีแก้ไขปัญหาทั่วไปอีกวิธีหนึ่งคือการปิดความสอดคล้องของธุรกรรมเมื่อไม่จำเป็นหรือเปลี่ยนแปลงระดับการแยกของคุณตัวอย่างเช่นงานที่ต้องใช้เวลานานในการคำนวณสถิติ ... คำตอบใกล้จะเพียงพอคุณไม่จำเป็นต้องใช้ตัวเลขที่แม่นยำ ตามที่พวกเขาเปลี่ยนจากคุณ และหากใช้เวลา 30 นาทีจึงจะเสร็จสมบูรณ์คุณไม่ต้องการให้มันหยุดทำธุรกรรมอื่น ๆ ทั้งหมดในตารางเหล่านั้น

...

สำหรับการติดตามมันขึ้นอยู่กับซอฟต์แวร์ฐานข้อมูลที่คุณใช้


มันเป็นเรื่องธรรมดาที่จะแสดงความคิดเห็นเมื่อ downvoting ... นี่เป็นคำตอบที่ถูกต้องคำสั่งที่เลือกให้อัพเกรดเป็นล็อคตารางและการใช้ตลอดไปอาจทำให้เกิดการหยุดชะงักได้
BlackICE

1
MS SQLServer สามารถให้พฤติกรรมการล็อคที่ไม่คาดคิดได้หากดัชนีไม่ได้ทำคลัสเตอร์ มันจะไม่สนใจทิศทางของคุณในการใช้การล็อกระดับแถวและจะทำการล็อกระดับหน้า จากนั้นคุณสามารถรอการหยุดชะงักในหน้า
Jay

7

เพียงเพื่อพัฒนาสิ่งเคอร์เซอร์ มันเลวร้ายจริงๆ มันล็อคตารางทั้งหมดแล้วประมวลผลแถวหนึ่งโดยหนึ่ง

วิธีที่ดีที่สุดคือให้เรียงแถวตามแนวเคอร์เซอร์โดยใช้การวนรอบสักครู่

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

รวมทั้งเร็วขึ้น ทำให้คุณสงสัยว่าทำไมถึงมีเคอร์เซอร์อยู่ดี

นี่คือตัวอย่างของโครงสร้างประเภทนี้:

DECLARE @LastID INT = (SELECT MAX(ID) FROM Tbl)
DECLARE @ID     INT = (SELECT MIN(ID) FROM Tbl)
WHILE @ID <= @LastID
    BEGIN
    IF EXISTS (SELECT * FROM Tbl WHERE ID = @ID)
        BEGIN
        -- Do something to this row of the table
        END

    SET @ID += 1  -- Don't forget this part!
    END

หากช่อง ID ของคุณเบาบางคุณอาจต้องการดึงรายการ ID แยกต่างหากและวนซ้ำตามนั้น:

DECLARE @IDs TABLE
    (
    Seq INT NOT NULL IDENTITY PRIMARY KEY,
    ID  INT NOT NULL
    )
INSERT INTO @IDs (ID)
    SELECT ID
    FROM Tbl
    WHERE 1=1  -- Criteria here

DECLARE @Rec     INT = 1
DECLARE @NumRecs INT = (SELECT MAX(Seq) FROM @IDs)
DECLARE @ID      INT
WHILE @Rec <= @NumRecs
    BEGIN
    SET @ID = (SELECT ID FROM @IDs WHERE Seq = @Seq)

    -- Do something to this row of the table

    SET @Seq += 1  -- Don't forget this part!
    END

6

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

การเพิ่มคำตอบอื่น ๆ ระดับการแยกธุรกรรมมีความสำคัญเนื่องจากการอ่านซ้ำและต่อเนื่องเป็นสิ่งที่ทำให้ล็อก 'อ่าน' ถูกเก็บไว้จนกว่าจะสิ้นสุดการทำธุรกรรม การล็อคทรัพยากรไม่ทำให้เกิดการหยุดชะงัก การทำให้มันล็อคไม่ทำงาน การดำเนินการเขียนเก็บทรัพยากรของตนไว้เสมอจนกว่าจะสิ้นสุดการทำธุรกรรม

กลยุทธ์การป้องกันการล็อคที่ชื่นชอบคือการใช้คุณสมบัติ 'ภาพรวม' คุณลักษณะ Read Committed Snapshot หมายความว่าการอ่านไม่ได้ใช้การล็อก! และหากคุณต้องการการควบคุมที่มากกว่า 'อ่านแล้ว' จะมีฟีเจอร์ 'ระดับการแยกสแนปช็อต' อันนี้อนุญาตให้เกิดธุรกรรมที่ต่อเนื่องกัน (โดยใช้คำศัพท์ MS ที่นี่) ในขณะที่ไม่ได้บล็อคผู้เล่นอื่น

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


-2

ในขณะที่เคอร์เซอร์ทำงานช้าใน SQL Server คุณสามารถหลีกเลี่ยงการหยุดชะงักในเคอร์เซอร์ด้วยการดึงข้อมูลต้นฉบับสำหรับเคอร์เซอร์ลงในตารางชั่วคราวและเรียกใช้เคอร์เซอร์บนมัน สิ่งนี้จะช่วยป้องกันไม่ให้เคอร์เซอร์ล็อคตารางข้อมูลจริงและการล็อคเพียงอย่างเดียวที่คุณได้รับคือการอัปเดตหรือการแทรกภายในเคอร์เซอร์ซึ่งจะถูกเก็บไว้เฉพาะในช่วงระยะเวลาของการแทรก / อัปเดตเท่านั้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.