ความแตกต่างระหว่างการอ่านแบบไม่สามารถทำซ้ำได้และการอ่านแบบ Phantom คืออะไร


155

ความแตกต่างระหว่างการอ่านที่ไม่สามารถทำซ้ำได้และการอ่าน phantom คืออะไร

ฉันได้อ่านบทความIsolation (ระบบฐานข้อมูล) จาก Wikipediaแล้ว แต่ฉันมีข้อสงสัยนิดหน่อย ในตัวอย่างด้านล่างสิ่งที่จะเกิดขึ้นคือไม่ใช่ทำซ้ำได้อ่านและผีอ่าน ?

ธุรกรรม
SELECT ID, USERNAME, accountno, amount FROM USERS WHERE ID=1
เอาท์พุท:
1----MIKE------29019892---------5000
ธุรกรรม B
UPDATE USERS SET amount=amount+5000 where ID=1 AND accountno=29019892;
COMMIT;
ธุรกรรม
SELECT ID, USERNAME, accountno, amount FROM USERS WHERE ID=1

อีกข้อสงสัยคือในตัวอย่างข้างต้นควรใช้ระดับการแยกแบบใด และทำไม?


คำตอบ:


166

จาก Wikipedia (ซึ่งมีตัวอย่างที่ยอดเยี่ยมและมีรายละเอียดสำหรับเรื่องนี้):

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

และ

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

ตัวอย่างง่ายๆ:

  • ผู้ใช้ A เรียกใช้แบบสอบถามเดียวกันสองครั้ง
  • ในระหว่างนั้นผู้ใช้ B ทำธุรกรรมและกระทำการ
  • ไม่สามารถอ่านซ้ำได้: แถว A ที่ผู้ใช้ A สอบถามมีค่าต่างกันเป็นครั้งที่สอง
  • อ่านผี: แถวทั้งหมดในแบบสอบถามมีค่าเดียวกันก่อนและหลังแต่มีการเลือกแถวที่แตกต่างกัน (เพราะ B ลบหรือแทรกบางส่วน) ตัวอย่าง: select sum(x) from table;จะส่งคืนผลลัพธ์ที่ต่างกันแม้ว่าจะไม่มีการอัปเดตแถวที่ได้รับผลกระทบใด ๆ ก็ตามหากมีการเพิ่มหรือลบแถว

ในตัวอย่างข้างต้นควรใช้ระดับการแยกใด

คุณต้องแยกระดับอะไรขึ้นอยู่กับใบสมัครของคุณ มีค่าใช้จ่ายสูงถึงระดับการแยก "ดีกว่า" (เช่นลดการเกิดพร้อมกัน)

ในตัวอย่างของคุณคุณจะไม่ได้อ่าน phantom เพราะคุณเลือกจากแถวเดียวเท่านั้น (ระบุโดยคีย์หลัก) คุณสามารถอ่านซ้ำได้ดังนั้นหากเป็นปัญหาคุณอาจต้องมีระดับการแยกที่ป้องกันได้ ใน Oracle ธุรกรรม A สามารถใช้ SELECT FOR UPDATE จากนั้นธุรกรรม B ไม่สามารถเปลี่ยนแถวได้จนกว่า A จะเสร็จสิ้น


6
ฉันไม่เข้าใจตรรกะของไวยากรณ์ดังกล่าวจริงๆ ... การอ่านที่ไม่สามารถทำซ้ำได้เกิดขึ้นเมื่อมีการอ่านซ้ำ (และได้ค่าที่แตกต่างกัน) ??! ...
serhio

14
@serhio "ไม่สามารถทำซ้ำได้" หมายถึงความจริงที่ว่าคุณสามารถอ่านค่าหนึ่งครั้งและรับค่า x เป็นผลลัพธ์จากนั้นอ่านอีกครั้งและรับค่า y เป็นผลลัพธ์ดังนั้นคุณจึงไม่สามารถทำซ้ำผลลัพธ์ที่เหมือนกันได้จากสองรายการ แบบสอบถามแยกต่างหากของแถวเดียวกันเนื่องจากค่าแถวนั้นได้รับการปรับปรุงในระหว่างการอ่าน
BateTech

@Thilo ตัวอย่างกรณีการใช้งานจริงใด ๆ ที่การอ่านซ้ำ ๆ ได้อาจสร้างปัญหาและสิ่งที่จำเป็น?
user104309

เกิดอะไรขึ้นถ้า PK ถูกแก้ไขในธุรกรรมอื่น อาจส่งผลให้อ่านผี? (สิ่งแปลกประหลาดที่ต้องทำในกรณีส่วนใหญ่ แต่เป็นไปไม่ได้)
jpmc26

1
ทั้งคู่ฟังดูเหมือนฉัน
sn.anurag

126

วิธีง่าย ๆ ที่ฉันชอบคิดเกี่ยวกับมันคือ:

ทั้งการอ่านที่ไม่สามารถทำซ้ำได้และ phantom นั้นเกี่ยวข้องกับการดำเนินการแก้ไขข้อมูลจากการทำธุรกรรมที่แตกต่างกันซึ่งเกิดขึ้นหลังจากการทำธุรกรรมของคุณเริ่มขึ้นแล้วอ่านโดยการทำธุรกรรมของคุณ

การอ่านที่ไม่สามารถทำซ้ำได้คือเมื่อการทำธุรกรรมของคุณอ่านUPDATESมุ่งมั่นจากธุรกรรมอื่น ตอนนี้แถวเดียวกันมีค่าแตกต่างจากตอนที่เริ่มทำธุรกรรม

การอ่านแบบ Phantom คล้ายกัน แต่เมื่ออ่านจากINSERTSและ / หรือDELETES ที่ได้ทำไว้จากธุรกรรมอื่น มีแถวหรือแถวใหม่ที่หายไปตั้งแต่คุณเริ่มทำธุรกรรม

การอ่านที่สกปรกคล้ายกับการอ่านที่ไม่สามารถทำซ้ำได้และการอ่านแบบ phantom แต่เกี่ยวข้องกับการอ่านข้อมูล UNCOMMITTED และเกิดขึ้นเมื่อมีการอ่าน UPDATE, INSERT หรือ DELETE จากธุรกรรมอื่นและธุรกรรมอื่นยังไม่ส่งข้อมูล กำลังอ่านข้อมูล "กำลังดำเนินการ" ซึ่งอาจไม่สมบูรณ์และอาจไม่ได้รับการยืนยัน


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

1
@PHPAvenger ที่นี่เป็นกรณีการใช้งานสำหรับระดับการอ่าน READ UNCOMMITTED: มีความเป็นไปได้ที่จะพบการหยุดชะงักระหว่างการเลือกและการอัพเดท (อธิบายที่นี่ ) หากแบบสอบถามแบบใช้เลือกข้อมูลมีความซับซ้อนเกินกว่าที่จะสร้างดัชนีครอบคลุมเพื่อหลีกเลี่ยงการหยุดชะงักคุณจะต้องใช้ระดับการแยกแบบอ่านไม่ได้รับพร้อมกับความเสี่ยงในการพบกับการอ่านที่สกปรก แต่บ่อยครั้งที่คุณย้อนกลับธุรกรรม ถาวรไหม!
petrica.martinescu

1
@ petrica.martinescu ปัญหาที่เกิดจากการอ่านสกปรกไม่เพียงเกี่ยวกับว่าธุรกรรมถูกย้อนกลับหรือไม่ การอ่านที่สกปรกสามารถส่งคืนผลลัพธ์ที่ไม่ถูกต้องมากขึ้นอยู่กับวิธีการแก้ไขข้อมูลในธุรกรรมที่รอดำเนินการ ลองนึกภาพการทำธุรกรรมที่ดำเนินการชุดของการลบการปรับปรุงและ / หรือการแทรกหลายรายการ หากคุณอ่านข้อมูลที่อยู่ตรงกลางของธุรกรรมนั้นโดยใช้ "read uncommitted" จะไม่สมบูรณ์ ระดับการแยก Snapshot (ใน SQL Server) เป็นทางเลือกที่ดีกว่ามากในการอ่านปราศจากข้อผูกมัด กรณีใช้งานที่ถูกต้องสำหรับระดับการแยกแบบไม่มีข้อผูกมัดการอ่านในระบบการผลิตเป็น IMO ที่หายาก
BateTech

2
@DiponRoy คำถามที่ดี การล็อกถูกนำมาใช้ถ้าใช้การแยกแบบอ่านซ้ำ (RR) ควรป้องกันการลบที่เกิดขึ้นบนแถวที่ถูกเลือก ฉันเคยเห็นคำจำกัดความที่แตกต่างกันของระดับ iso 2 ตลอดหลายปีที่ผ่านมาส่วนใหญ่บอกว่า phantom คือการเปลี่ยนแปลงในคอลเลกชัน / แถว # กลับมาและ RR คือแถวเดียวกันที่มีการเปลี่ยนแปลง ฉันเพิ่งตรวจสอบเอกสาร MS SQL ที่อัปเดตแล้วบอกว่าการลบอาจทำให้ไม่ใช่-RR ( docs.microsoft.com/en-us/sql/odbc/reference/develop-app/ ...... ) ดังนั้นฉันคิดว่ามันปลอดภัยที่จะลบกลุ่มใน หมวดหมู่ RR เช่นกัน
BateTech

2
@anir ใช่การแทรกและการลบจะรวมอยู่ในการอ่านที่สกปรก ตัวอย่าง: เริ่มทำธุรกรรมแทรก 2 บรรทัดจาก 100 ใบแจ้งหนี้ในการเชื่อมต่อ a ตอนนี้การเชื่อมต่อ b อ่าน 2 บรรทัดก่อนที่จะมีการมอบหมาย trx และก่อนที่จะเพิ่ม 98 บรรทัดอื่น ๆ ดังนั้นจึงไม่รวมข้อมูลทั้งหมดสำหรับใบแจ้งหนี้ นี่จะเป็นการอ่านที่สกปรกที่เกี่ยวข้องกับส่วนแทรก
BateTech

28

ตามที่อธิบายไว้ในบทความนี้ที่ไม่ซ้ำอ่านความผิดปกติมีลักษณะดังต่อไปนี้:

ป้อนคำอธิบายรูปภาพที่นี่

  1. Alice และ Bob เริ่มธุรกรรมฐานข้อมูลสองรายการ
  2. Bob's อ่านบันทึกโพสต์และค่าคอลัมน์ชื่อเรื่องคือ Transaction
  3. อลิซแก้ไขชื่อของบันทึกโพสต์ที่กำหนดเป็นค่าของกรด
  4. อลิซกระทำธุรกรรมฐานข้อมูลของเธอ
  5. หากบ๊อบอ่านบันทึกการโพสต์อีกครั้งเขาจะสังเกตเห็นแถวตารางรุ่นอื่น

ในบทความเกี่ยวกับPhantom Readคุณจะเห็นว่าความผิดปกตินี้เกิดขึ้นดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่

  1. Alice และ Bob เริ่มธุรกรรมฐานข้อมูลสองรายการ
  2. Bob's อ่านบันทึก post_comment ทั้งหมดที่เกี่ยวข้องกับแถวโพสต์ที่มีค่าตัวระบุเป็น 1
  3. Alice เพิ่มระเบียน post_comment ใหม่ซึ่งเชื่อมโยงกับแถวโพสต์ที่มีค่าตัวระบุเป็น 1
  4. อลิซกระทำธุรกรรมฐานข้อมูลของเธอ
  5. หาก Bob อ่านบันทึก post_comment อีกครั้งโดยมีค่าคอลัมน์ post_id เท่ากับ 1 เขาจะสังเกตเห็นชุดผลลัพธ์รุ่นอื่น

ดังนั้นในขณะที่การอ่านที่ไม่สามารถทำซ้ำได้จะใช้กับแถวเดียว แต่Phantom Readเป็นช่วงของเร็กคอร์ดที่ตรงตามเกณฑ์การกรองแบบสอบถามที่กำหนด


3
การสร้างภาพชั้นยอด @Vlad
dextermini

23

อ่านปรากฏการณ์

  • การอ่านที่สกปรก : อ่านข้อมูลที่ไม่ได้รับคำสั่งจากธุรกรรมอื่น
  • ไม่สามารถอ่านซ้ำได้ : อ่านข้อมูล COMMITTED จากUPDATEแบบสอบถามจากธุรกรรมอื่น
  • Phantom อ่าน : อ่านข้อมูล COMMITTED จากINSERTหรือDELETEแบบสอบถามจากธุรกรรมอื่น

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

นอกจากนี้เราอาจสังเกตว่าการอัปเดตอาจเป็นงานที่บ่อยขึ้นในกรณีการใช้งานส่วนใหญ่มากกว่า INSERT หรือ DELETES จริง (ในกรณีเช่นนี้, อันตรายจาก การอ่านที่ไม่สามารถทำซ้ำได้ยังคงอยู่เท่านั้น - การอ่าน phantomไม่สามารถทำได้ในกรณีเหล่านั้น) นี่คือสาเหตุที่การอัปเดตได้รับการปฏิบัติแตกต่างจาก INSERT-DELETE และความผิดปกติที่เกิดขึ้นก็มีชื่อแตกต่างกันเช่นกัน

นอกจากนี้ยังมีค่าใช้จ่ายในการประมวลผลเพิ่มเติมที่เกี่ยวข้องกับการจัดการสำหรับ INSERT-DELETE แทนที่จะเป็นเพียงแค่จัดการ UPDATES


ประโยชน์ที่แตกต่าง การแยกระดับที่

  • READ_UNCOMMITTED ป้องกันอะไรเลย มันเป็นระดับการแยกศูนย์
  • READ_COMMITTED ป้องกันเพียงหนึ่งเดียวนั่นคือ Dirty reads
  • REPEATABLE_READ ป้องกันความผิดปกติสองอย่าง: อ่านสกปรกและอ่านซ้ำไม่ได้
  • SERIALIZABLE ป้องกันความผิดปกติทั้งสาม: อ่านสกปรกอ่านซ้ำไม่ได้และอ่าน Phantom

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

ในความเป็นจริงการใช้เวลาธุรกรรมอยู่ในอัตราต่อไปนี้:

SERIALIZABLE > REPEATABLE_READ > READ_COMMITTED > READ_UNCOMMITTED

ดังนั้นการตั้งค่า READ_UNCOMMITTED เป็นเร็วที่สุด


สรุป

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

โปรดทราบว่าฐานข้อมูลโดยค่าเริ่มต้นจะมีการตั้งค่า REPEATABLE_READ


1
UPDATE หรือ DELETE ทั้งสองสามารถใช้แทนการอ่านที่ไม่สามารถทำซ้ำได้หรือเป็นเพียงการอัพเดตเท่านั้น?
Dipon Roy

1
UPDATE หรือ DELETE ทั้งสองสามารถใช้แทนการอ่านที่ไม่สามารถทำซ้ำได้
niket patel

ที่จริงแล้วเราสามารถสรุปได้ว่าโดยเฉลี่ยแล้วคำสั่ง DELETE แบบสุ่มที่ดำเนินการโดยธุรกรรมอื่นในฐานข้อมูลเดียวกันนั้นมีความเป็นไปได้ต่ำมากที่จะไม่สามารถอ่านซ้ำได้สำหรับธุรกรรมปัจจุบัน แต่คำสั่งลบเดียวกันมีโอกาส 100% ที่ทำให้ Phantom อ่านธุรกรรมปัจจุบัน ถ้ามองอย่างนั้นการเขียนของฉันผิดไปนิดหน่อยถ้าคุณเอามาพูดแทนคำ แต่เดี๋ยวก่อนฉันตั้งใจเขียนด้วยวิธีนี้เพื่อทำให้สิ่งที่ชัดเจนยิ่งขึ้นกับผู้อ่าน
Subhadeep Ray

+1 สำหรับคำอธิบายที่ง่ายและเข้าใจง่าย อย่างไรก็ตามฉันคิดว่าฐานข้อมูลส่วนใหญ่ (oracle, mysql) มีระดับการแยกเริ่มต้นเป็น Read Committed และ postgress อาจใช้ค่าเริ่มต้นของ repeatable_read
akila

7

มีความแตกต่างในการนำไปใช้ระหว่างระดับการแยกสองชนิดนี้
สำหรับ "การอ่านที่ไม่สามารถทำซ้ำได้" จำเป็นต้องล็อคแถว
สำหรับ "phantom read", การกำหนดขอบเขตการล็อกจำเป็นต้องมีแม้กระทั่งการล็อคตาราง
เราสามารถใช้สองระดับนี้โดยใช้โปรโตคอลล็อคสองเฟส


ในการใช้การอ่านซ้ำหรือทำซ้ำได้ไม่จำเป็นต้องใช้การล็อกแถว
a_horse_with_no_name

5

ในระบบที่ไม่สามารถอ่านซ้ำได้ผลลัพธ์ของการสืบค้นครั้งที่สองของทรานแซคชัน A จะสะท้อนการอัพเดทในทรานแซคชัน B - ซึ่งจะเห็นจำนวนใหม่

ในระบบที่อนุญาตให้ phantom อ่านหากทรานแซคชัน B แทรกแถวใหม่ด้วย ID = 1 ธุรกรรม A จะเห็นแถวใหม่เมื่อมีการเรียกใช้คิวรีที่สอง เช่นการอ่าน phantom เป็นกรณีพิเศษของการอ่านที่ไม่สามารถทำซ้ำได้


ฉันไม่คิดว่าคำอธิบายของ phantom read นั้นถูกต้อง คุณสามารถรับการอ่านแบบ phantom ได้แม้ว่าจะไม่เห็นข้อมูลที่ไม่ได้กระทำก็ตาม ดูตัวอย่างใน Wikipedia (ลิงก์ในความคิดเห็นด้านบน)
Thilo

1

คำตอบที่ได้รับการยอมรับแสดงให้เห็นว่าส่วนใหญ่ของความแตกต่างที่เรียกว่าระหว่างทั้งสองนั้นไม่สำคัญเลย

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

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


0

ฉันคิดว่ามีความแตกต่างระหว่างการอ่านไม่สามารถยกเลิกได้และการอ่านออกเสียง

ไม่สามารถยกเลิกได้หมายความว่ามีการทำธุรกรรม A & B หาก B สามารถสังเกตเห็นการเปลี่ยนแปลงของ A ดังนั้นอาจเกิดการอ่านสกปรกดังนั้นเราจึงให้ B สังเกตเห็นการเปลี่ยนแปลงของ A หลังจาก A กระทำ

มีปัญหาใหม่: เราให้ B สังเกตเห็นการแก้ไข A หลังจาก A ที่กระทำ, หมายถึง A แก้ไขค่าของแถวที่ B ถืออยู่, บางครั้ง B จะอ่านแถวอีกครั้ง, ดังนั้น B จะได้รับค่าใหม่ที่แตกต่างกันในครั้งแรกที่เรา ได้รับเราเรียกมันว่ายกเลิกไม่ได้เพื่อจัดการกับปัญหาเราปล่อยให้ B จำบางสิ่ง (เพราะฉันยังไม่รู้ว่าจะจำอะไรได้บ้าง) เมื่อ B เริ่มต้น

ลองคิดเกี่ยวกับวิธีแก้ปัญหาใหม่เราสามารถสังเกตเห็นว่ามีปัญหาใหม่ด้วยทำให้เราให้ B จำบางอย่างดังนั้นสิ่งที่เกิดขึ้นใน A, B จะไม่ได้รับผลกระทบ แต่ถ้า B ต้องการแทรกข้อมูลลงในตารางและ B ตรวจสอบตารางเพื่อให้แน่ใจว่าไม่มีบันทึก แต่ A ข้อมูลนี้ถูกแทรกโดย A ดังนั้นอาจเกิดข้อผิดพลาดบางอย่าง เราเรียกมันว่า Phantom-read


0

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

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