ระดับการแยกธุรกรรมสัมพันธ์กับการล็อกบนโต๊ะ


105

ฉันได้อ่านเกี่ยวกับการแยก 4 ระดับ:

Isolation Level       Dirty Read    Nonrepeatable Read  Phantom Read  
READ UNCOMMITTED      Permitted       Permitted           Permitted
READ COMMITTED              --        Permitted           Permitted
REPEATABLE READ             --             --             Permitted
SERIALIZABLE                --             --              --

ฉันต้องการเข้าใจการล็อกการแยกธุรกรรมแต่ละรายการเกิดขึ้นบนโต๊ะ

READ UNCOMMITTED - no lock on table
READ COMMITTED - lock on committed data
REPEATABLE READ - lock on block of sql(which is selected by using select query)
SERIALIZABLE - lock on full table(on which Select query is fired)

ด้านล่างนี้เป็นปรากฏการณ์สามประการที่สามารถเกิดขึ้นได้ในการแยกธุรกรรม
Dirty Read - ไม่มีการล็อกการอ่านแบบไม่ทำ
ซ้ำ - ไม่มีการอ่านแบบสกปรกเมื่อล็อกข้อมูลที่ถูก ผูกมัด
Phantom Read - ล็อคบนบล็อกของ sql (ซึ่งถูกเลือกโดยใช้แบบสอบถามแบบเลือก)

ฉันต้องการเข้าใจว่าเรากำหนดระดับการแยกเหล่านี้ไว้ที่ใด: เฉพาะที่ระดับ jdbc / hibernate หรือใน DB ด้วย

PS: ฉันได้ดูลิงก์ในระดับการแยกใน Oracleแต่พวกเขาดูเงอะงะและพูดคุยเกี่ยวกับฐานข้อมูลเฉพาะ


3
ทั้งหมดนี้ขึ้นอยู่กับฐานข้อมูล ฐานข้อมูลที่แตกต่างกันอาจใช้อัลกอริทึมที่แตกต่างกันสำหรับระดับการแยก บางคนอาจใช้ MVCC (ไม่มีการล็อกในแบบสอบถามที่เลือก) บางส่วนใช้การล็อก 2 เฟสที่เข้มงวด (การล็อกแบบใช้ร่วมกันและแบบพิเศษ)
brb tea

คำตอบ:


157

ฉันต้องการเข้าใจการล็อกการแยกธุรกรรมแต่ละรายการเกิดขึ้นบนโต๊ะ

ตัวอย่างเช่นคุณมี 3 กระบวนการที่เกิดขึ้นพร้อมกัน A, B และ C A เริ่มต้นธุรกรรมเขียนข้อมูลและคอมมิต / ย้อนกลับ (ขึ้นอยู่กับผลลัพธ์) B เพียงรันSELECTคำสั่งเพื่ออ่านข้อมูล C อ่านและอัปเดตข้อมูล กระบวนการทั้งหมดนี้ทำงานบนตารางเดียวกัน T.

  • อ่านไม่ถูกต้อง - ไม่มีการล็อกบนโต๊ะ คุณสามารถอ่านข้อมูลในตารางได้ในขณะที่เขียนอยู่ ซึ่งหมายความว่า A เขียนข้อมูล (ไม่ถูกผูกมัด) และ B สามารถอ่านข้อมูลที่ไม่ถูกผูกมัดนี้และใช้ (เพื่อวัตถุประสงค์ใด ๆ ) หาก A ดำเนินการย้อนกลับแสดงว่า B ยังคงอ่านข้อมูลและนำไปใช้ นี่เป็นวิธีที่เร็วที่สุด แต่ไม่ปลอดภัยที่สุดในการทำงานกับข้อมูลเนื่องจากอาจนำไปสู่ช่องว่างของข้อมูลในตารางที่ไม่เกี่ยวข้องกันทางกายภาพ (ใช่ตารางสองตารางอาจมีเหตุผล แต่ไม่เกี่ยวข้องทางกายภาพในแอปจริง = \)
  • อ่านคอมมิต - ล็อกข้อมูลที่ผูกมัด คุณสามารถอ่านข้อมูลที่มีการคอมมิตเท่านั้น ซึ่งหมายความว่า A เขียนข้อมูลและ B ไม่สามารถอ่านข้อมูลที่ A บันทึกไว้ได้จนกว่า A จะดำเนินการคอมมิต ปัญหาคือ C สามารถอัปเดตข้อมูลที่อ่านและใช้บนไคลเอนต์ B และ B จะไม่มีข้อมูลที่อัปเดต
  • REPEATABLE READ - ล็อกบล็อกของ SQL (ซึ่งถูกเลือกโดยใช้คิวรีแบบเลือก) ซึ่งหมายความว่า B อ่านข้อมูลภายใต้เงื่อนไขบางประการเช่นWHERE aField > 10 AND aField < 20A แทรกข้อมูลที่มีaFieldค่าอยู่ระหว่าง 10 ถึง 20 จากนั้น B อ่านข้อมูลอีกครั้งและได้ผลลัพธ์ที่แตกต่างกัน
  • SERIALIZABLE - ล็อกบนตารางเต็ม (ที่เลือกคิวรีจะเริ่มทำงาน) ซึ่งหมายความว่า B อ่านข้อมูลและไม่มีธุรกรรมอื่นใดที่สามารถแก้ไขข้อมูลบนตารางได้ นี่เป็นวิธีที่ปลอดภัยที่สุด แต่ช้าที่สุดในการทำงานกับข้อมูล นอกจากนี้เนื่องจากการดำเนินการอ่านอย่างง่ายจะล็อกตารางสิ่งนี้อาจทำให้เกิดปัญหาหนักในการผลิต: ลองนึกภาพว่า T table เป็นตารางใบแจ้งหนี้ผู้ใช้ X ต้องการทราบใบแจ้งหนี้ของวันและผู้ใช้ Y ต้องการสร้างใบแจ้งหนี้ใหม่ดังนั้น ในขณะที่ X ดำเนินการอ่านใบแจ้งหนี้ Y ไม่สามารถเพิ่มใบแจ้งหนี้ใหม่ได้ (และเมื่อเป็นเรื่องเงินผู้คนจะคลั่งไคล้โดยเฉพาะเจ้านาย)

ฉันต้องการเข้าใจว่าเรากำหนดระดับการแยกเหล่านี้ไว้ที่ใด: เฉพาะที่ระดับ JDBC / hibernate หรือใน DB ด้วย

ใช้ JDBC คุณกำหนดโดยใช้Connection#setTransactionIsolation.

ใช้ไฮเบอร์เนต:

<property name="hibernate.connection.isolation">2</property>

ที่ไหน

  • 1: อ่าน UNCOMMITTED
  • 2: อ่านคำมั่นสัญญา
  • 4: อ่านซ้ำได้
  • 8: เป็นทางการ

การกำหนดค่าไฮเบอร์เนตนำมาจากที่นี่ (ขออภัยเป็นภาษาสเปน)

อย่างไรก็ตามคุณสามารถตั้งค่าระดับการแยกบน RDBMS ได้เช่นกัน:

และต่อไป ...


docs.oracle.com/cd/B12037_01/server.101/b10743/consist.htm เพื่อเพิ่มสำหรับ Oracle: หนึ่งสามารถตั้งค่าระดับการแยกของธุรกรรมโดยใช้หนึ่งในคำสั่งเหล่านี้เมื่อเริ่มต้นธุรกรรม: SET TRANSACTION ISOLATION ระดับการอ่านที่มอบหมาย; ตั้งค่าระดับการแยกธุรกรรมที่สามารถใช้งานได้; ตั้งค่าการทำธุรกรรมอ่านเท่านั้น;
ผู้เรียน

2
นอกจากนี้เพื่อประหยัดค่าเครือข่ายและค่าดำเนินการในการเริ่มต้นธุรกรรมแต่ละรายการด้วยคำสั่ง SET TRANSACTION คุณสามารถใช้คำสั่ง ALTER SESSION เพื่อตั้งค่าระดับการแยกธุรกรรมสำหรับธุรกรรมที่ตามมาทั้งหมด: ALTER SESSION SET ISOLATION_LEVEL SERIALIZABLE; แก้ไขการตั้งค่าเซสชัน ISOLATION_LEVEL อ่านคอมมิต;
ผู้เรียน

12
เกี่ยวกับการอ่านซ้ำ - ฉันคิดว่าตัวอย่างที่ดีกว่าที่จะแสดงให้เห็นมีดังต่อไปนี้: B เริ่มต้นธุรกรรมอ่านข้อมูลบนบล็อกของ sql WHERE aField> 10 และ aField <20 ข้อมูลนั้นจะถูกล็อคจนกว่าธุรกรรมจะสิ้นสุดลง A พยายามอัปเดตข้อมูลนั้น แต่รอเพราะล็อก ตอนนี้เมื่อ B จะอ่านข้อมูลนั้นอีกครั้งในธุรกรรมเดียวกันนั้นรับประกันว่าจะอ่านข้อมูลเดียวกันเพราะถูกล็อก ช่วยแก้ให้ด้วยนะถ้าฉันผิด.
BornToCode

1
@LuiggiMendoza ในฐานะที่เป็นแนวคิดทั่วไประดับแยกเป็นเพียงเกี่ยวกับสกปรกอ่าน , ไม่ซ้ำอ่านและแถวผี Locks (S2PL) หรือ MVCC เป็นการใช้งานสำหรับผู้ขายที่แตกต่างกัน
brb tea

4
@LuiggiMendoza - ฉันไม่ถูกต้องมันควรจะเป็นแบบนี้ - ข้อมูลที่ B อ่านจะไม่เปลี่ยนแปลง แต่ผลที่ตามมาการเลือกโดย B อาจส่งคืนแถวได้มากกว่า นั่นเป็นเพราะA ไม่สามารถแก้ไขแถวที่ B อ่านไปแล้วจนกว่า A จะปล่อยแถวนั้น อย่างไรก็ตาม A สามารถแทรกแถวใหม่ที่มีคุณสมบัติตรงตามเงื่อนไข (ดังนั้นในครั้งต่อไป A จะดำเนินการเลือกมันจะได้ผลลัพธ์ที่แตกต่างกันโดยมีแถวมากขึ้น - การอ่าน Phantom)
BornToCode

9

ดังที่ brb tea กล่าวขึ้นอยู่กับการใช้งานฐานข้อมูลและอัลกอริทึมที่ใช้: MVCC หรือ Two Phase Locking

CUBRID (โอเพ่นซอร์ส RDBMS) อธิบายแนวคิดของอัลกอริทึมทั้งสองนี้:

  • การล็อคสองเฟส (2PL)

รายการแรกคือเมื่อธุรกรรม T2 พยายามเปลี่ยนระเบียน A จะรู้ว่าธุรกรรม T1 ได้เปลี่ยนระเบียน A แล้วและรอจนกว่าธุรกรรม T1 จะเสร็จสมบูรณ์เนื่องจากธุรกรรม T2 ไม่สามารถทราบได้ว่าธุรกรรม T1 จะถูกผูกมัดหรือรีด กลับ. วิธีนี้เรียกว่าการล็อคสองเฟส (2PL)

  • การควบคุมการทำงานพร้อมกันหลายเวอร์ชัน (MVCC)

อีกวิธีหนึ่งคืออนุญาตให้แต่ละธุรกรรม T1 และ T2 มีเวอร์ชันที่เปลี่ยนแปลงของตนเอง แม้ว่าธุรกรรม T1 จะเปลี่ยนเร็กคอร์ด A จาก 1 เป็น 2 ทรานแซคชัน T1 จะทิ้งค่าเดิม 1 ตามที่เป็นอยู่และเขียนว่าเวอร์ชันธุรกรรม T1 ของเร็กคอร์ด A คือ 2 จากนั้นธุรกรรม T2 ต่อไปนี้จะเปลี่ยนเร็กคอร์ด A จาก 1 ถึง 3 ไม่ใช่ตั้งแต่ 2 ถึง 4 และเขียนว่าเวอร์ชันธุรกรรม T2 ของเร็กคอร์ด A คือ 3

เมื่อทรานแซคชัน T1 ถูกย้อนกลับไม่สำคัญว่า 2 เวอร์ชันธุรกรรม T1 จะไม่ถูกนำไปใช้กับเรกคอร์ด A หลังจากนั้นหากมีการคอมมิตธุรกรรม T2 3 ซึ่งเป็นเวอร์ชันธุรกรรม T2 จะถูกนำไปใช้กับเรกคอร์ด A หากมีการทำธุรกรรม T1 ก่อนการทำธุรกรรม T2 ระเบียน A จะเปลี่ยนเป็น 2 และจากนั้นเป็น 3 ในขณะที่ทำธุรกรรม T2 สถานะฐานข้อมูลขั้นสุดท้ายจะเหมือนกับสถานะของการดำเนินการแต่ละธุรกรรมโดยอิสระโดยไม่มีผลกระทบต่อธุรกรรมอื่น ๆ ดังนั้นจึงเป็นไปตามคุณสมบัติของกรด วิธีนี้เรียกว่า Multi-version concurrency control (MVCC)

MVCC อนุญาตให้ทำการแก้ไขพร้อมกันโดยมีค่าใช้จ่ายเพิ่มขึ้นในหน่วยความจำ (เนื่องจากต้องรักษาเวอร์ชันต่างๆของข้อมูลเดียวกัน) และการคำนวณ (ในระดับ REPETEABLE_READ คุณจะไม่สามารถอัปเดตหลวมได้ดังนั้นจึงต้องตรวจสอบเวอร์ชันของข้อมูลเช่น Hiberate ทำกับOptimistick Locking )

ในระดับการแยกธุรกรรม 2PL ควบคุมสิ่งต่อไปนี้ :

  • มีการล็อกเมื่ออ่านข้อมูลหรือไม่และมีการร้องขอการล็อกประเภทใด

  • ล็อคการอ่านจะเก็บไว้นานแค่ไหน

  • การดำเนินการอ่านที่อ้างถึงแถวที่แก้ไขโดยธุรกรรมอื่นหรือไม่:

    • บล็อกจนกว่าการล็อกพิเศษในแถวจะถูกปลดปล่อย

    • ดึงข้อมูลเวอร์ชันที่มุ่งมั่นของแถวที่มีอยู่ในขณะที่คำสั่งหรือธุรกรรมเริ่มต้น

    • อ่านการแก้ไขข้อมูลที่ไม่ได้ผูกมัด

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

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

ตัวอย่างที่เป็นรูปธรรมของความสัมพันธ์ระหว่างการล็อกและระดับการแยกในSQL Server (ใช้ 2PL ยกเว้นบน READ_COMMITED กับ READ_COMMITTED_SNAPSHOT = ON)

  • READ_UNCOMMITED: อย่าออกการล็อกที่ใช้ร่วมกันเพื่อป้องกันไม่ให้ธุรกรรมอื่นแก้ไขข้อมูลที่อ่านโดยธุรกรรมปัจจุบัน READ UNCOMMITTED ธุรกรรมจะไม่ถูกปิดกั้นโดยการล็อกพิเศษที่จะป้องกันไม่ให้ธุรกรรมปัจจุบันอ่านแถวที่ได้รับการแก้ไข แต่ไม่ได้กระทำโดยธุรกรรมอื่น [... ]

  • READ_COMMITED:

    • ถ้า READ_COMMITTED_SNAPSHOT ถูกตั้งค่าเป็น OFF (ค่าเริ่มต้น): ใช้การล็อกที่ใช้ร่วมกันเพื่อป้องกันไม่ให้ธุรกรรมอื่นแก้ไขแถวในขณะที่ธุรกรรมปัจจุบันกำลังดำเนินการอ่าน การล็อกที่ใช้ร่วมกันยังบล็อกคำสั่งไม่ให้อ่านแถวที่แก้ไขโดยธุรกรรมอื่นจนกว่าธุรกรรมอื่นจะเสร็จสมบูรณ์ [... ] การล็อกแถวจะถูกปลดก่อนที่จะประมวลผลแถวถัดไป [... ]
    • ถ้า READ_COMMITTED_SNAPSHOT ถูกตั้งค่าเป็นเปิดโปรแกรมฐานข้อมูลจะใช้การกำหนดเวอร์ชันของแถวเพื่อนำเสนอแต่ละคำสั่งที่มีสแน็ปช็อตของข้อมูลที่สอดคล้องกันตามธุรกรรมที่มีอยู่ในตอนเริ่มต้นของคำสั่ง ไม่ได้ใช้การล็อกเพื่อป้องกันข้อมูลจากการอัปเดตโดยธุรกรรมอื่น ๆ
  • REPETEABLE_READ: การล็อกที่ใช้ร่วมกันจะถูกวางไว้บนข้อมูลทั้งหมดที่อ่านโดยแต่ละคำสั่งในธุรกรรมและจะเก็บไว้จนกว่าธุรกรรมจะเสร็จสมบูรณ์

  • SERIALIZABLE: การล็อกช่วงจะอยู่ในช่วงของค่าคีย์ที่ตรงกับเงื่อนไขการค้นหาของแต่ละคำสั่งที่ดำเนินการในธุรกรรม [... ] การล็อกช่วงจะถูกระงับจนกว่าธุรกรรมจะเสร็จสมบูรณ์


5

การล็อกจะอยู่ที่ระดับ DB เสมอ: -

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

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

ตัวอย่างหนึ่งของระดับการแยกธุรกรรมคือ TRANSACTION_READ_COMMITTED ซึ่งจะไม่อนุญาตให้เข้าถึงค่าจนกว่าจะมีการคอมมิต กล่าวอีกนัยหนึ่งถ้าระดับการแยกธุรกรรมถูกตั้งค่าเป็น TRANSACTION_READ_COMMITTED DBMS จะไม่อนุญาตให้เกิดการอ่านสกปรก การเชื่อมต่ออินเทอร์เฟซประกอบด้วยค่าห้าค่าที่แสดงถึงระดับการแยกธุรกรรมที่คุณสามารถใช้ใน JDBC

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