ควรใช้ SELECT … FOR UPDATE เมื่อใด


119

SELECT ... FOR UPDATEโปรดช่วยให้ฉันเข้าใจที่อยู่เบื้องหลังกรณีการใช้งาน

คำถามที่ 1ต่อไปนี้เป็นตัวอย่างที่ดีว่าSELECT ... FOR UPDATEควรใช้เมื่อใด?

ได้รับ:

  • ห้อง [ID]
  • แท็ก [id, name]
  • room_tags [room_id, tag_id]
    • room_id และ tag_id เป็นคีย์ต่างประเทศ

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

  • ในขั้นต้น:
    • ห้องประกอบด้วย [id = 1]
    • แท็กประกอบด้วย [id = 1, name = 'cats']
    • room_tags ประกอบด้วย [room_id = 1, tag_id = 1]
  • หัวข้อที่ 1: SELECT id FROM rooms;
    • returns [id = 1]
  • หัวข้อที่ 2: DELETE FROM room_tags WHERE room_id = 1;
  • หัวข้อที่ 2: DELETE FROM rooms WHERE id = 1;
  • หัวข้อที่ 2: [ทำธุรกรรม]
  • หัวข้อที่ 1: SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
    • ส่งคืนรายการว่าง

ตอนนี้กระทู้ 1 คิดว่าห้อง 1 ไม่มีแท็ก แต่ในความเป็นจริงห้องนั้นถูกลบออกไปแล้ว เพื่อแก้ปัญหานี้เธรด 1 ควรSELECT id FROM rooms FOR UPDATEป้องกันเธรด 2 จากการลบออกroomsไปจนกว่าเธรด 1 จะเสร็จสิ้น ถูกต้องหรือไม่

คำถามที่ 2 : เมื่อใดควรใช้SERIALIZABLEการแยกธุรกรรมเทียบREAD_COMMITTEDกับSELECT ... FOR UPDATE?

คำตอบคาดว่าจะพกพาได้ (ไม่ใช่เฉพาะฐานข้อมูล) หากไม่สามารถทำได้โปรดอธิบายสาเหตุ


2
คุณใช้ RDBMS ใด
Quassnoi

2
@Quassnoi ตามที่ระบุไว้ที่ด้านล่างของคำถามฉันกำลังมองหาโซลูชันแบบพกพา (ไม่ใช่เฉพาะฐานข้อมูล)
Gili

2
ตัวเลือกREPEATABLE_READและREAD_COMMITTEDตัวเลือกพกพาหรือไม่? ผลลัพธ์เดียวที่ฉันได้รับสำหรับเซิร์ฟเวอร์ MSSQL
Billy ONeal

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

3
A select ... for updateon roomsยังคงอนุญาตให้room_tagsลบได้เนื่องจากเป็นตารางแยกกัน คุณหมายถึงถามว่าfor updateประโยคจะป้องกันการลบroomsหรือไม่?
Chris Saxon

คำตอบ:


84

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

อย่างไรก็ตามในบางระบบการล็อกเป็นผลข้างเคียงของการควบคุมการทำงานพร้อมกันและคุณได้ผลลัพธ์เดียวกันโดยไม่ต้องระบุFOR UPDATEอย่างชัดเจน


เพื่อแก้ปัญหานี้เธรด 1 ควรSELECT id FROM rooms FOR UPDATEป้องกันเธรด 2 จากการลบออกroomsไปจนกว่าเธรด 1 จะเสร็จสิ้น ถูกต้องหรือไม่

ขึ้นอยู่กับการควบคุมพร้อมกันที่ระบบฐานข้อมูลของคุณใช้

  • MyISAMในMySQL(และระบบเก่าอื่น ๆ อีกหลายระบบ) จะล็อกทั้งตารางในช่วงเวลาของแบบสอบถาม

  • ในSQL Server, SELECTคำสั่งสถานที่ล็อคในรายการ / หน้า / ตารางพวกเขาได้รับการตรวจสอบร่วมกันในขณะที่DMLการปรับปรุงคำสั่งสถานล็อค (ซึ่งต่อมาได้รับการเลื่อนตำแหน่งให้เป็นพิเศษหรือลดไปล็อคที่ใช้ร่วมกัน) การล็อกแบบพิเศษไม่สามารถใช้ร่วมกับการล็อกที่ใช้ร่วมกันได้ดังนั้นการล็อกแบบใดแบบหนึ่งSELECTหรือการDELETEค้นหาจะล็อกจนกว่าเซสชันอื่นจะทำงาน

  • ในฐานข้อมูลที่ใช้งานMVCC(เช่นOracle, PostgreSQL, MySQLด้วยInnoDB) ซึ่งเป็นDMLแบบสอบถามสร้างสำเนาของการบันทึก (ในหนึ่งหรืออีกวิธีหนึ่ง) และโดยทั่วไปผู้อ่านไม่ปิดกั้นนักเขียนและในทางกลับกัน สำหรับฐานข้อมูลเหล่านี้SELECT FOR UPDATEจะมีประโยชน์: มันจะล็อกอย่างใดอย่างหนึ่งSELECTหรือDELETEแบบสอบถามจนกว่าเซสชันอื่นSQL Serverจะยอมรับเช่นเดียวกับที่ทำ

เมื่อใดที่ควรใช้REPEATABLE_READการแยกธุรกรรมเทียบREAD_COMMITTEDกับSELECT ... FOR UPDATE?

โดยทั่วไปREPEATABLE READไม่ห้ามแถวผี (แถวที่ปรากฏหรือหายไปในธุรกรรมอื่นแทนที่จะแก้ไข)

  • ในOracleและPostgreSQLเวอร์ชันก่อนหน้าREPEATABLE READนี้เป็นคำพ้องความหมายของSERIALIZABLE. โดยทั่วไปหมายความว่าธุรกรรมจะไม่เห็นการเปลี่ยนแปลงที่เกิดขึ้นหลังจากเริ่มต้นแล้ว ดังนั้นในการตั้งค่านี้Thread 1ข้อความค้นหาสุดท้ายจะส่งคืนห้องราวกับว่าห้องนั้นไม่เคยถูกลบ (ซึ่งอาจใช่หรือไม่ใช่สิ่งที่คุณต้องการ) หากคุณไม่ต้องการแสดงห้องหลังจากที่ถูกลบไปแล้วคุณควรล็อกแถวด้วยSELECT FOR UPDATE

  • ในInnoDB, REPEATABLE READและSERIALIZABLEเป็นสิ่งที่แตกต่างกัน: ผู้อ่านในSERIALIZABLEชุดล็อคโหมดต่อไปที่สำคัญเกี่ยวกับการบันทึกพวกเขาประเมินประสิทธิภาพการป้องกันพร้อมกันDMLกับพวกเขา ดังนั้นคุณจึงไม่จำเป็นต้องมีSELECT FOR UPDATEในโหมด serializable แต่ไม่จำเป็นพวกเขาในหรือREPEATABLE READREAD COMMITED

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

เมื่อฉันพูดว่า "คุณไม่ต้องการSELECT FOR UPDATE" ฉันควรจะเพิ่มจริงๆ "เนื่องจากผลข้างเคียงของการใช้เครื่องมือฐานข้อมูลบางอย่าง"


1
ประเด็นสุดท้ายคือปมของเรื่องนี้ฉันคิดว่า: "คุณไม่จำเป็นต้องเลือกเพื่ออัปเดตในโหมดซีเรียลไลซ์ได้ แต่ต้องใช้ในการอ่านซ้ำหรือ READ COMMITED"
Colin 't Hart

คุณถูก. คำถามที่สองควรจะได้ถามเมื่อSERIALIZABLEควรจะใช้เมื่อเทียบกับREAD_COMMITTED SELECT ... FOR UPDATEคุณช่วยอัปเดตคำตอบของคุณให้ตรงกับคำถามที่อัปเดตนี้ได้ไหม
Gili

1
@Gili: "คุณไม่จำเป็นต้องSELECT FOR UPDATEอยู่ในโหมด serializable" InnoDBกับ กับอีกระบบที่สองเป็นคำพ้องความหมายและคุณจำเป็นต้องทำMVCC SELECT FOR UPDATE
Quassnoi

1
ฉันพบว่าโพสต์ของ Colin ตอบคำถามเฉพาะของฉันได้ดีกว่าคำตอบของคุณ แต่ฉันขอขอบคุณข้อมูลอ้างอิงทั้งหมดที่คุณให้มา ฉันจะยอมรับคำตอบที่รวมทั้งสองอย่างเข้าด้วยกันได้ดีที่สุด (คำตอบเฉพาะด้านบนสนับสนุนการอ้างอิงด้านล่าง)
Gili

This depends on the concurrency control your database system is using: ฉันคิดว่าคุณกำลังแยกผม ทุกกรณีที่คุณระบุไว้ด้านล่างนี้จะบอกว่าห้องนั้นจะไม่ถูกลบระหว่างSELECTถึงจุดสิ้นสุดของธุรกรรม ดังนั้นคำตอบควรมาYesจากข้อมูลอ้างอิงด้านล่างนี้ไม่ใช่หรือ?
Gili

33

คำตอบสั้น ๆ:

Q1: ใช่

Q2: ไม่สำคัญว่าคุณจะใช้อะไร

คำตอบยาว:

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

อีกวิธีหนึ่งในการดูมันเหมือนกับว่าสองคำสั่งต่อไปนี้ถูกเรียกใช้งานแบบอะตอม:

select * from my_table where my_condition;

update my_table set my_column = my_column where my_condition;

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

โปรดทราบด้วยว่าระดับการแยกธุรกรรมไม่ขึ้นอยู่กับการล็อกการตั้งค่าระดับการแยกที่แตกต่างกันไม่อนุญาตให้คุณหลีกเลี่ยงการล็อกและอัปเดตแถวในธุรกรรมอื่นที่ธุรกรรมของคุณถูกล็อก

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


1
ฉันคิดว่าเป็นWhat transaction isolation levels do guarantee [...] is the consistency of data once transactions are completed.นัยที่ไม่ถูกต้องว่าระดับการแยกไม่ส่งผลต่อสิ่งที่เกิดขึ้นระหว่างการทำธุรกรรม ขอแนะนำให้แก้ไขส่วนนี้และให้รายละเอียดเพิ่มเติมว่าส่วนนี้ส่งผลต่อสิ่งที่คุณเห็น (หรือไม่เห็น) อย่างไรในระหว่างการทำธุรกรรม
Gili

1
ฉันพบว่าโพสต์ของคุณตอบคำถามเฉพาะของฉันได้ดีกว่าของ Quassnoiแต่ฉันขอขอบคุณข้อมูลอ้างอิงทั้งหมดที่เขาให้มา ฉันจะยอมรับคำตอบที่รวมทั้งสองอย่างเข้าด้วยกันได้ดีที่สุด (คำตอบเฉพาะด้านบนสนับสนุนการอ้างอิงด้านล่าง)
Gili

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