0Pricing
SQL Academy · บทเรียน

เดดล็อก: การตรวจจับและการหลีกเลี่ยง

ทำความเข้าใจว่าเดดล็อกเกิดขึ้นได้อย่างไร Postgres ตรวจจับเดดล็อกอย่างไร และออกแบบกฎลำดับการล็อกเพื่อป้องกันเดดล็อก

เดดล็อก: การตรวจจับและการหลีกเลี่ยง เป็นบทเรียน SQL Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน SQL Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส SQL Academy มีบทเรียนทั้งหมด 4 บทเรียน

ภาวะล็อกตายคืออะไร

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

ภาวะล็อกตายแบบคลาสสิก

ธุรกรรม A ล็อกแถวที่ 1 ส่วนธุรกรรม B ล็อกแถวที่ 2 จากนั้น A ขอแถวที่ 2 และ B ขอแถวที่ 1 ทั้งสองจึงติดอยู่

-- Tx A:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- waiting for B...

-- Tx B:
BEGIN;
UPDATE accounts SET balance = balance - 50 WHERE id = 2;
UPDATE accounts SET balance = balance + 50 WHERE id = 1;
-- waiting for A...

-- ERROR: deadlock detected

PostgreSQL ตรวจจับภาวะล็อกตาย

ทุก ๆ deadlock_timeout (ค่าเริ่มต้น 1 วินาที) PostgreSQL จะตรวจสอบวงจรการล็อก หากพบ จะยกเลิกธุรกรรมหนึ่งรายการพร้อมรหัสข้อผิดพลาด 40P01

ERROR:  deadlock detected
DETAIL:  Process 1234 waits for ShareLock on transaction 5678 ...

กฎการเรียงลำดับการล็อก

วิธีแก้คือ ต้องขอการล็อกตามลำดับเดียวกันเสมอในทุกเส้นทางของโค้ด

-- Always update the lower id first:
UPDATE accounts SET balance = balance - 100 WHERE id = LEAST(:from, :to);
UPDATE accounts SET balance = balance + 100 WHERE id = GREATEST(:from, :to);

ภาวะล็อกตายจากแถวที่มีการใช้งานหนาแน่น

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

FOR UPDATE ล็อกแถวที่อ่าน

ขอการล็อกเพื่อเขียนตั้งแต่เวลาอ่าน เพื่อหลีกเลี่ยงความประหลาดใจในภายหลัง:

BEGIN;
SELECT * FROM accounts WHERE id IN (1, 2) ORDER BY id FOR UPDATE;
-- both rows locked in id order
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

ข้ามแถวที่ถูกล็อก

สำหรับตารางคิว ให้ใช้รูปแบบ "หยิบแถวใดก็ได้ที่พร้อมใช้งาน":

SELECT * FROM jobs
WHERE status = 'pending'
ORDER BY created_at
LIMIT 1
FOR UPDATE SKIP LOCKED;
-- skips rows other workers have locked

NOWAIT

ล้มเหลวทันทีแทนที่จะรอ:

SELECT * FROM accounts WHERE id = 1 FOR UPDATE NOWAIT;
-- ERROR: could not obtain lock on row in relation "accounts"

การวินิจฉัยภาวะล็อกตาย

เพิ่มค่า log_lock_waits และเก็บบริบทของภาวะล็อกตายไว้ใน log รายการใน log จะแสดงธุรกรรมทั้งสองรายการและ query ของแต่ละรายการ

วงวนการลองใหม่ในแอปพลิเคชัน

ภาวะล็อกตายสามารถกู้คืนได้ ให้ลองธุรกรรมที่ถูกยกเลิกใหม่:

for (let attempt = 0; attempt < 3; attempt++) {
  try {
    await runTransaction();
    break;
  } catch (e) {
    if (e.code === '40P01') continue;     // deadlock
    throw e;
  }
}

ลดขอบเขตการล็อก

ทำให้ธุรกรรมสั้นลง ทุกแถวที่ถูกแตะต้องจะยังถูกล็อกไว้จนกว่าจะถึง COMMIT อย่าเรียกใช้ HTTP หรือประมวลผลเป็นเวลานานภายในธุรกรรม

สร้างดัชนีให้คีย์ต่างประเทศเพื่อหลีกเลี่ยงการขยายขอบเขตการล็อก

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

สรุปทบทวน

ภาวะล็อกตายเกิดขึ้นได้ จึงควรออกแบบเพื่อลดโอกาสเกิด

  • ขอการล็อกตามลำดับที่สอดคล้องกัน
  • ใช้ FOR UPDATE ตั้งแต่เนิ่น ๆ เพื่อประกาศความต้องการ
  • ใช้ SKIP LOCKED สำหรับคิว
  • ลองใหม่เมื่อเกิดข้อผิดพลาดจากภาวะล็อกตาย (40P01)
  • ทำให้ธุรกรรมสั้น

แบบทดสอบสั้น ๆ

หลักการออกแบบใดน่าเชื่อถือที่สุดในการป้องกันภาวะล็อกตาย

คำถามที่พบบ่อย

บทเรียน “เดดล็อก: การตรวจจับและการหลีกเลี่ยง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “เดดล็อก: การตรวจจับและการหลีกเลี่ยง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส SQL Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส SQL Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “เดดล็อก: การตรวจจับและการหลีกเลี่ยง”

ทำความเข้าใจว่าเดดล็อกเกิดขึ้นได้อย่างไร Postgres ตรวจจับเดดล็อกอย่างไร และออกแบบกฎลำดับการล็อกเพื่อป้องกันเดดล็อก คุณปฏิบัติ SQL Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน SQL Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน SQL Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “เดดล็อก: การตรวจจับและการหลีกเลี่ยง” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน SQL Academy นี้ได้ไหม

ได้ บทเรียน SQL Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. คุณสมบัติ ACID และความผิดปกติ
  2. ระดับการแยก: READ COMMITTED, REPEATABLE READ, SERIALIZABLE
  3. เดดล็อก: การตรวจจับและการหลีกเลี่ยง
  4. การล็อกแบบมองโลกในแง่ดีเทียบกับแบบระมัดระวัง
← กลับไปที่ SQL Academy