0Pricing
SQL Interview Prep · บทเรียน

การอ่านข้อมูลสกปรก การอ่านซ้ำไม่ได้ และการอ่านข้อมูลแฝง

ความผิดปกติในการอ่านทั้งสามแบบ และระดับการแยกออกจากกันที่ป้องกันแต่ละแบบ

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

ความผิดปกติของการอ่านสามประเภท

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

  • การอ่านข้อมูลสกปรก - การอ่านข้อมูลที่ยังไม่ได้ยืนยัน
  • การอ่านที่ให้ผลไม่ซ้ำเดิม - แถวหนึ่งเปลี่ยนแปลงระหว่างการอ่านสองครั้ง
  • การอ่านแถวแฝง - มีแถวใหม่ปรากฏระหว่างการอ่านสองครั้ง

เคล็ดลับคือการแยกความแตกต่างระหว่างการอ่านที่ให้ผลไม่ซ้ำเดิมกับการอ่านแถวแฝง เพราะทั้งสองแบบเกี่ยวข้องกับการค้นข้อมูลซ้ำแล้วได้ผลลัพธ์ต่างกัน

การอ่านข้อมูลสกปรก: คำจำกัดความ

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

มีเพียง READ UNCOMMITTED เท่านั้นที่ยอมให้เกิดการอ่านข้อมูลสกปรกได้ ระดับที่สูงกว่าทุกระดับจะป้องกันการอ่านแบบนี้

อันตรายในโลกจริง: อนุมัติเงินกู้โดยอาศัยเงินฝากที่ถูกยกเลิกในอีกไม่กี่วินาทีต่อมา

การอ่านข้อมูลสกปรก: ลำดับเวลา

ให้อ่านสองคอลัมน์ตามลำดับเวลา T1 ทำงานที่ระดับ READ UNCOMMITTED

T1 เห็นยอดคงเหลือ 700 แต่ T2 ไม่เคยยืนยันการทำรายการ ค่า 700 เป็นข้อมูลลวงจากงานที่ T2 กำลังดำเนินการอยู่ หลังจาก T2 ยกเลิกการทำรายการ ค่าจริงยังคงเป็น 500 T1 จึงตัดสินใจจากข้อมูลที่ใช้ไม่ได้

-- T2 (not committed)        | -- T1 (READ UNCOMMITTED)
BEGIN;                       |
UPDATE accounts              |
  SET balance = 700          |
  WHERE id = 1;              |
                             | SELECT balance FROM accounts
                             |   WHERE id = 1;  -- reads 700 (dirty!)
ROLLBACK;                    |
                             | -- T1 acted on a value that never existed

การอ่านที่ให้ผลไม่ซ้ำเดิม: คำจำกัดความ

การอ่านที่ให้ผลไม่ซ้ำเดิม เกิดขึ้นเมื่อ T1 อ่านแถวหนึ่ง T2 ยืนยัน การแก้ไขหรือลบแถวนั้น และ T1 อ่านแถวเดิมอีกครั้งแล้วเห็นค่าแตกต่างไป

สังเกตข้อแตกต่างสำคัญจากการอ่านข้อมูลสกปรก: ในกรณีนี้ T2 ยืนยันแล้ว ข้อมูลนั้นมีอยู่จริง แต่เปลี่ยนแปลงไปภายในธุรกรรมเดียวของ T1

READ COMMITTED ยังคงยอมให้เกิดเหตุการณ์นี้ได้ ส่วน REPEATABLE READ และระดับที่สูงกว่าจะป้องกันเหตุการณ์นี้ด้วยการอ่านจากภาพรวมที่คงที่

การอ่านที่ให้ผลไม่ซ้ำเดิม: ลำดับเวลา

T1 ทำงานที่ระดับ READ COMMITTED และอ่านแถวเดียวกันสองครั้ง ระหว่างการอ่านทั้งสองครั้ง T2 ยืนยันการเปลี่ยนแปลง

คีย์หลักเดียวกันกลับให้ค่าต่างกันสองค่าภายในธุรกรรมเดียว ความไม่สอดคล้องนี้อาจทำให้ตรรกะหลายขั้นตอนที่คาดว่าแถวจะคงเดิมทำงานผิดพลาด

-- T1 (READ COMMITTED)              | -- T2
BEGIN;                              |
SELECT balance FROM accounts        |
  WHERE id = 1;  -- 500            |
                                    | BEGIN;
                                    | UPDATE accounts SET balance = 900
                                    |   WHERE id = 1;
                                    | COMMIT;
SELECT balance FROM accounts        |
  WHERE id = 1;  -- 900 (changed!) |
COMMIT;                             |

การอ่านแถวแฝง: คำจำกัดความ

การอ่านแถวแฝง เกิดขึ้นเมื่อ T1 เรียกใช้คำสั่งค้นหาที่มีเงื่อนไขการค้นหา T2 ยืนยัน การ INSERT (หรือ DELETE) แถวที่ตรงตามเงื่อนไขนั้น และ T1 เรียกใช้คำสั่งค้นหาเดิมอีกครั้งแล้วเห็น ชุดแถว ที่แตกต่างไป

ข้อแตกต่างจากการอ่านที่ให้ผลไม่ซ้ำเดิมคือ การอ่านที่ให้ผลไม่ซ้ำเดิมเกี่ยวกับค่าของแถวที่มีอยู่เดิม เปลี่ยนแปลง ส่วนการอ่านแถวแฝงเกี่ยวกับ จำนวนแถว ที่ตรงตามเงื่อนไข เปลี่ยนแปลง

ตามมาตรฐาน มีเพียง SERIALIZABLE เท่านั้นที่รับประกันว่าจะป้องกันการอ่านแถวแฝงได้

การอ่านแถวแฝง: ลำดับเวลา

T1 นับบัญชีที่มีมูลค่าสูงสองครั้ง ระหว่างการนับ T2 เพิ่มแถวใหม่ที่ตรงตามเงื่อนไขและยืนยันการทำรายการ

แม้ไม่มีแถวเดิมเปลี่ยนแปลง แต่ COUNT กลับต่างกัน แถวใหม่คือ "แถวลวง" ที่ปรากฏในชุดผลลัพธ์ของ T1

-- T1 (REPEATABLE READ, standard)      | -- T2
BEGIN;                                 |
SELECT COUNT(*) FROM accounts           |
  WHERE balance > 1000;  -- 3          |
                                       | INSERT INTO accounts(id, balance)
                                       |   VALUES (99, 5000);
                                       | COMMIT;
SELECT COUNT(*) FROM accounts           |
  WHERE balance > 1000;  -- 4 (phantom)|
COMMIT;                                |

การจับคู่ความผิดปกติกับระดับ

การจับคู่นี้เป็นหัวใจของหัวข้อนี้ ระดับต่ำสุดที่ป้องกันความผิดปกติแต่ละประเภทได้คือ:

  • การอ่านข้อมูลสกปรก ป้องกันได้ตั้งแต่ READ COMMITTED ขึ้นไป
  • การอ่านที่ให้ผลไม่ซ้ำเดิม ป้องกันได้ตั้งแต่ REPEATABLE READ ขึ้นไป
  • การอ่านแถวแฝง ป้องกันได้ด้วย SERIALIZABLE ตามมาตรฐาน

สังเกตว่าชื่อสอดคล้องกัน: REPEATABLE READ ทำให้การอ่านสามารถทำซ้ำแล้วได้ผลเดิม ระดับต่าง ๆ จึงตั้งชื่อตามความผิดปกติที่ระดับนั้นสามารถป้องกันได้เพิ่มเติม

การอ่านที่ให้ผลไม่ซ้ำเดิมกับการอ่านแถวแฝง: จุดแบ่งสำคัญ

นี่คือจุดที่ผู้สมัครมักสับสนกันมากที่สุด จดจำประโยคนี้ไว้:

การอ่านที่ให้ผลไม่ซ้ำเดิม = ค่าของแถวเดิมเปลี่ยนแปลง การอ่านแถวแฝง = ชุดแถวที่ตรงตามเงื่อนไขเปลี่ยนแปลง (มีการเพิ่มหรือลบแถว)

ลองตรวจสอบความเข้าใจ: T2 เรียกใช้ UPDATE ... WHERE id = 5 แล้วยืนยันการทำรายการ จากนั้น T1 อ่านแถวที่ 5 ซ้ำ นั่นคือการอ่านที่ให้ผลไม่ซ้ำเดิม ส่วน T2 เรียกใช้ INSERT เพื่อเพิ่มแถวใหม่ที่ตรงตาม WHERE ของ T1 แล้ว T1 เรียกใช้คำสั่งค้นหาเดิมอีกครั้ง นั่นคือการอ่านแถวแฝง

การเขียนแบบเอนเอียง: ความผิดปกติเพิ่มเติม

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

ตัวอย่างคลาสสิกคือ แพทย์สองคนเข้าเวร แพทย์แต่ละคนตรวจสอบว่ามีแพทย์อีกคนเข้าเวรอยู่ แล้วจึงถอนตัวออกจากเวร ทั้งคู่ทำสำเร็จ ส่งผลให้ไม่มีแพทย์เข้าเวรเลย

การแยกธุรกรรมแบบภาพรวม (โพสต์เกรส REPEATABLE READ) ยอมให้เกิดการเขียนแบบเอนเอียงได้ มีเพียง SERIALIZABLE เท่านั้นที่ป้องกันได้ การกล่าวถึงประเด็นนี้แสดงว่าคุณเข้าใจหัวข้อนี้ในระดับลึก

การปรับปรุงที่สูญหาย: กับดักข้อที่สี่

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

ตัวอย่างเช่น การโอนเงินสองรายการต่างอ่านยอดคงเหลือ 500 ต่างหักจำนวนเงินออก แล้วต่างเขียนผลลัพธ์ของตนกลับมา ผลการหักหนึ่งรายการจึงสูญหาย

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

-- Safe pattern: lock the row, or compute atomically
BEGIN;
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;  -- locks row
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
-- Or simply: UPDATE accounts SET balance = balance - 100 WHERE id = 1;

ตรวจสอบอย่างรวดเร็ว

ระบุความผิดปกติจากพฤติกรรมของมัน

สรุป: ความผิดปกติและวิธีแก้

ความผิดปกติของการอ่านสามประเภท แต่ละประเภทแก้ได้ด้วยระดับการแยกธุรกรรมที่สูงขึ้น:

  • การอ่านข้อมูลสกปรก (ข้อมูลที่ยังไม่ได้ยืนยัน) - แก้ได้ที่ READ COMMITTED
  • การอ่านที่ให้ผลไม่ซ้ำเดิม (ค่าของแถวเดิมเปลี่ยนแปลง) - แก้ได้ที่ REPEATABLE READ
  • การอ่านแถวแฝง (ชุดแถวที่ตรงตามเงื่อนไขเปลี่ยนแปลง) - แก้ได้ที่ SERIALIZABLE

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

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

บทเรียน “การอ่านข้อมูลสกปรก การอ่านซ้ำไม่ได้ และการอ่านข้อมูลแฝง” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “การอ่านข้อมูลสกปรก การอ่านซ้ำไม่ได้ และการอ่านข้อมูลแฝง”

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

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

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

บทเรียน “การอ่านข้อมูลสกปรก การอ่านซ้ำไม่ได้ และการอ่านข้อมูลแฝง” ใช้เวลานานแค่ไหน

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

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

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

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

  1. อธิบายคุณสมบัติ ACID
  2. ระดับการแยกออกจากกันทั้งสี่ระดับ
  3. การอ่านข้อมูลสกปรก การอ่านซ้ำไม่ได้ และการอ่านข้อมูลแฝง
  4. ภาวะติดตาย การล็อก และ MVCC
← กลับไปที่ SQL Interview Prep