การอ่านข้อมูลสกปรก การอ่านซ้ำไม่ได้ และการอ่านข้อมูลแฝง
ความผิดปกติในการอ่านทั้งสามแบบ และระดับการแยกออกจากกันที่ป้องกันแต่ละแบบ
การอ่านข้อมูลสกปรก การอ่านซ้ำไม่ได้ และการอ่านข้อมูลแฝง เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- อธิบายคุณสมบัติ ACID
- ระดับการแยกออกจากกันทั้งสี่ระดับ
- การอ่านข้อมูลสกปรก การอ่านซ้ำไม่ได้ และการอ่านข้อมูลแฝง
- ภาวะติดตาย การล็อก และ MVCC