จะจับ SqlException ที่เกิดจากการหยุดชะงักได้อย่างไร?


94

จากแอป. NET 3.5 / C # ฉันต้องการตรวจจับSqlExceptionแต่ถ้าเกิดจากการหยุดชะงักในอินสแตนซ์ SQL Server 2008

ข้อความแสดงข้อผิดพลาดทั่วไปคือ Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

แต่ดูเหมือนว่าจะไม่ใช่รหัสข้อผิดพลาดในเอกสารสำหรับข้อยกเว้นนี้

การกรองข้อยกเว้นไม่ให้มีคีย์เวิร์ดdeadlockในข้อความดูเหมือนเป็นวิธีที่น่าเกลียดมากในการบรรลุพฤติกรรมนี้ มีใครรู้วิธีที่ถูกต้องในการทำเช่นนี้หรือไม่?


3
ฉัน (สุดท้าย) พบเอกสารสำหรับรหัสข้อผิดพลาด: msdn.microsoft.com/en-us/library/aa337376.aspx คุณสามารถค้นหาสิ่งนี้ผ่าน SQL Server เอง:select * from master.dbo.sysmessages where error=1205
Martin McNulty

คำตอบ:


157

รหัสข้อผิดพลาดเฉพาะของ Microsft SQL Server สำหรับการหยุดชะงักคือ 1205 ดังนั้นคุณต้องจัดการ SqlException และตรวจสอบสิ่งนั้น ดังนั้นเช่นถ้าสำหรับ SqlException ประเภทอื่น ๆ ทั้งหมดที่คุณต้องการให้เกิดฟองขึ้น

catch (SqlException ex)
{
    if (ex.Number == 1205)
    {
        // Deadlock 
    }
    else
        throw;
}

หรือใช้การกรองข้อยกเว้นที่มีอยู่ใน C # 6

catch (SqlException ex) when (ex.Number == 1205)
{
    // Deadlock 
}

สิ่งที่สะดวกในการค้นหารหัสข้อผิดพลาด SQL จริงสำหรับข้อความที่ระบุคือการค้นหาใน sys.messages ใน SQL Server

เช่น

SELECT * FROM sys.messages WHERE text LIKE '%deadlock%' AND language_id=1033

อีกวิธีหนึ่งในการจัดการกับการชะงักงัน (จาก SQL Server 2005 ขึ้นไป) คือการดำเนินการภายในกระบวนงานที่เก็บไว้โดยใช้ TRY ... CATCH สนับสนุน:

BEGIN TRY
    -- some sql statements
END TRY
BEGIN CATCH
    IF (ERROR_NUMBER() = 1205)
        -- is a deadlock
    ELSE
        -- is not a deadlock
END CATCH

มีตัวอย่างทั้งหมดที่นี่ใน MSDN เกี่ยวกับวิธีการใช้ลอจิกการลองล็อกใหม่ทั้งหมดภายใน SQL


2
โปรดทราบว่ารหัสข้อผิดพลาดเป็นรหัสเฉพาะของผู้จัดจำหน่ายดังนั้น 1205 จึงเป็นการหยุดชะงักสำหรับ SQL Server แต่อาจแตกต่างกันสำหรับ Oracle, MySQL และอื่น ๆ
brianmearns

3
ขึ้นอยู่กับชั้นข้อมูลชั้นข้อมูลSqlExceptionอาจถูกรวมไว้ในชั้นข้อมูลอื่น ดังนั้นเราอาจจำเป็นต้องจับชนิดยกเว้นใด ๆ InnerExceptionและตรวจสอบพวกเขาแล้วถ้าพวกเขาไม่ได้โดยตรงยกเว้นการหยุดชะงักของพวกเขาตรวจสอบซ้ำ
Frédéric

46

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

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

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

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


4
ทำไมคุณถึงต้องการการเชื่อมต่อใหม่ทั้งหมด? ผมเคยโพสต์คำถามเกี่ยวกับคำตอบนี้ที่นี่
แซม

3

นี่คือวิธี C # 6 ในการตรวจจับการหยุดชะงัก

try
{
    //todo: Execute SQL. 
    //IMPORTANT, if you used Connection.BeginTransaction(), this try..catch must surround that code. You must rollback the original transaction, then recreate it and re-run all the code.
}
catch (SqlException ex) when (ex.Number == 1205)
{
    //todo: Retry SQL
}

ตรวจสอบให้แน่ใจว่าการลองนี้ .. จับรอบธุรกรรมทั้งหมด ตาม @Steven (ดูคำตอบของเขาสำหรับรายละเอียด) เมื่อคำสั่ง sql ล้มเหลวเนื่องจากการหยุดชะงักจะทำให้ธุรกรรมถูกย้อนกลับและหากคุณไม่สร้างธุรกรรมขึ้นมาใหม่การลองใหม่ของคุณจะดำเนินการนอกบริบทของ ธุรกรรมและอาจส่งผลให้ข้อมูลไม่สอดคล้องกัน

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