InterSystems IRIS DBMS รองรับโครงสร้างที่น่าสนใจสำหรับการจัดเก็บข้อมูล - ทั่วโลก โดยพื้นฐานแล้ว คีย์เหล่านี้เป็นคีย์หลายระดับที่มาพร้อมกับคุณสมบัติเพิ่มเติมมากมายในรูปแบบของธุรกรรม ฟังก์ชันที่รวดเร็วสำหรับการสำรวจแผนผังข้อมูล การล็อค และภาษา ObjectScript ของตัวเอง
อ่านเพิ่มเติมเกี่ยวกับ globals ในบทความชุด “Globals are a Treasure-swords for storage data”:
ฉันเริ่มสนใจว่าธุรกรรมถูกนำไปใช้อย่างไรใน globals มีฟีเจอร์อะไรบ้าง ท้ายที่สุดแล้วนี่เป็นโครงสร้างการจัดเก็บข้อมูลที่แตกต่างไปจากตารางที่คุ้นเคยอย่างสิ้นเชิง ระดับต่ำกว่ามาก
ดังที่ทราบจากทฤษฎีฐานข้อมูลเชิงสัมพันธ์ การดำเนินการธุรกรรมที่ดีต้องเป็นไปตามข้อกำหนด :
เอ - อะตอม (อะตอมมิก) การเปลี่ยนแปลงทั้งหมดที่เกิดขึ้นในการทำธุรกรรมหรือไม่มีการบันทึกเลย
C - ความสม่ำเสมอ หลังจากธุรกรรมเสร็จสมบูรณ์ สถานะลอจิคัลของฐานข้อมูลจะต้องสอดคล้องกันภายใน ข้อกำหนดนี้เกี่ยวข้องกับโปรแกรมเมอร์หลายประการ แต่ในกรณีของฐานข้อมูล SQL ยังเกี่ยวข้องกับคีย์ต่างประเทศด้วย
ฉัน - แยก ธุรกรรมที่ทำงานแบบขนานไม่ควรมีผลกระทบต่อกัน
D - ทนทาน หลังจากธุรกรรมเสร็จสมบูรณ์ ปัญหาในระดับที่ต่ำกว่า (เช่น ไฟฟ้าดับ) ไม่ควรส่งผลกระทบต่อข้อมูลที่เปลี่ยนแปลงโดยธุรกรรม
Globals เป็นโครงสร้างข้อมูลที่ไม่สัมพันธ์กัน พวกมันได้รับการออกแบบมาให้ทำงานเร็วเป็นพิเศษบนฮาร์ดแวร์ที่มีจำนวนจำกัดมาก มาดูการดำเนินการธุรกรรมใน globals โดยใช้ .
เพื่อรองรับธุรกรรมใน IRIS จะใช้คำสั่งต่อไปนี้: , , .
1. อะตอมมิก
วิธีตรวจสอบที่ง่ายที่สุดคืออะตอมมิกซิตี เราตรวจสอบจากคอนโซลฐานข้อมูล
Kill ^a
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3
TCOMMITจากนั้นเราก็สรุป:
Write ^a(1), “ ”, ^a(2), “ ”, ^a(3)เราได้รับ:
1 2 3ทุกอย่างปกติดี. คงความเป็นอะตอมมิกเอาไว้: การเปลี่ยนแปลงทั้งหมดจะถูกบันทึกไว้
มาทำให้งานซับซ้อนขึ้น เกิดข้อผิดพลาด และดูว่าธุรกรรมได้รับการบันทึกอย่างไร บางส่วนหรือไม่บันทึกเลย
มาตรวจสอบอะตอมมิกซิตีอีกครั้ง:
Kill ^A
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3จากนั้นเราจะหยุดคอนเทนเนอร์อย่างแรง เปิดมันออกมาดู
docker kill my-irisคำสั่งนี้เกือบจะเทียบเท่ากับการบังคับปิดระบบ เนื่องจากจะส่งสัญญาณ SIGKILL เพื่อหยุดกระบวนการทันที
บางทีธุรกรรมอาจถูกบันทึกไว้บางส่วน?
WRITE ^a(1), ^a(2), ^a(3)
^
<UNDEFINED> ^a(1)- ไม่ มันไม่รอด
ลองใช้คำสั่งย้อนกลับ:
Kill ^A
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3
TROLLBACK
WRITE ^a(1), ^a(2), ^a(3)
^
<UNDEFINED> ^a(1)ไม่มีอะไรรอดเช่นกัน
2. ความสม่ำเสมอ
เนื่องจากในฐานข้อมูลที่ยึดตาม globals จึงมีการสร้างคีย์บน globals ด้วย (ฉันขอเตือนคุณว่า global เป็นโครงสร้างระดับต่ำกว่าสำหรับการจัดเก็บข้อมูลมากกว่าตารางเชิงสัมพันธ์) เพื่อให้เป็นไปตามข้อกำหนดด้านความสอดคล้อง การเปลี่ยนแปลงในคีย์จะต้องรวมอยู่ด้วย ในการทำธุรกรรมเดียวกันกับการเปลี่ยนแปลงระดับโลก
ตัวอย่างเช่น เรามี ^person ทั่วโลก ซึ่งเราจัดเก็บบุคลิกภาพและใช้ TIN เป็นกุญแจ
^person(1234567, ‘firstname’) = ‘Sergey’
^person(1234567, ‘lastname’) = ‘Kamenev’
^person(1234567, ‘phone’) = ‘+74995555555
...เพื่อให้ค้นหาได้อย่างรวดเร็วด้วยนามสกุลและชื่อ เราจึงสร้างคีย์ ^index
^index(‘Kamenev’, ‘Sergey’, 1234567) = 1เพื่อให้ฐานข้อมูลสอดคล้องกัน เราต้องเพิ่มบุคคลดังนี้:
TSTART
^person(1234567, ‘firstname’) = ‘Sergey’
^person(1234567, ‘lastname’) = ‘Kamenev’
^person(1234567, ‘phone’) = ‘+74995555555
^index(‘Kamenev’, ‘Sergey’, 1234567) = 1
TCOMMITดังนั้นเมื่อทำการลบ เราต้องใช้ธุรกรรมด้วย:
TSTART
Kill ^person(1234567)
ZKill ^index(‘Kamenev’, ‘Sergey’, 1234567)
TCOMMITกล่าวอีกนัยหนึ่ง การปฏิบัติตามข้อกำหนดด้านความสอดคล้องนั้นขึ้นอยู่กับไหล่ของโปรแกรมเมอร์ทั้งหมด แต่เมื่อพูดถึงระดับโลก นี่เป็นเรื่องปกติ เนื่องจากมีลักษณะเป็นระดับต่ำ
3. การแยกตัว
นี่คือจุดเริ่มต้นของความดุร้าย ผู้ใช้จำนวนมากทำงานพร้อมกันบนฐานข้อมูลเดียวกัน โดยเปลี่ยนแปลงข้อมูลเดียวกัน
สถานการณ์นี้เทียบได้กับการที่ผู้ใช้จำนวนมากทำงานพร้อมกันกับที่เก็บโค้ดเดียวกัน และพยายามคอมมิตการเปลี่ยนแปลงกับไฟล์หลายไฟล์พร้อมกัน
ฐานข้อมูลควรจัดเรียงข้อมูลทั้งหมดแบบเรียลไทม์ เมื่อพิจารณาว่าในบริษัทที่จริงจัง ยังมีบุคคลพิเศษที่รับผิดชอบการควบคุมเวอร์ชัน (สำหรับการรวมสาขา การแก้ไขข้อขัดแย้ง ฯลฯ) และฐานข้อมูลจะต้องดำเนินการทั้งหมดนี้แบบเรียลไทม์ ความซับซ้อนของงานและความถูกต้องของ การออกแบบฐานข้อมูลและโค้ดที่รองรับ
ฐานข้อมูลไม่สามารถเข้าใจความหมายของการกระทำที่ผู้ใช้ทำเพื่อหลีกเลี่ยงข้อขัดแย้งหากพวกเขากำลังทำงานกับข้อมูลเดียวกัน สามารถยกเลิกธุรกรรมหนึ่งที่ขัดแย้งกับอีกธุรกรรมหนึ่งหรือดำเนินการตามลำดับได้เท่านั้น
ปัญหาอีกประการหนึ่งคือในระหว่างการทำธุรกรรม (ก่อนคอมมิต) สถานะของฐานข้อมูลอาจไม่สอดคล้องกัน ดังนั้นจึงเป็นที่พึงปรารถนาที่ธุรกรรมอื่น ๆ จะไม่สามารถเข้าถึงสถานะที่ไม่สอดคล้องกันของฐานข้อมูล ซึ่งทำได้ในฐานข้อมูลเชิงสัมพันธ์ ในหลาย ๆ ด้าน: การสร้างสแน็ปช็อต แถวหลายเวอร์ชัน และอื่น ๆ
เมื่อทำธุรกรรมแบบคู่ขนาน สิ่งสำคัญสำหรับเราคือต้องไม่รบกวนซึ่งกันและกัน นี่คือคุณสมบัติของการแยก
SQL กำหนดระดับการแยก 4 ระดับ:
- อ่านไม่เข้าใจ
- อ่านคำมั่นสัญญา
- อ่านซ้ำได้
- ซีเรียลไลซ์ได้
มาดูแต่ละระดับแยกกัน ค่าใช้จ่ายในการดำเนินการแต่ละระดับเพิ่มขึ้นเกือบเท่าทวีคูณ
อ่านไม่เข้าใจ - นี่คือระดับการแยกต่ำสุด แต่ในขณะเดียวกันก็เร็วที่สุด ธุรกรรมสามารถอ่านการเปลี่ยนแปลงที่ทำร่วมกันได้
อ่านคำมั่นสัญญา คือการโดดเดี่ยวอีกระดับหนึ่ง ซึ่งเป็นการประนีประนอม ธุรกรรมไม่สามารถอ่านการเปลี่ยนแปลงของกันและกันก่อนการคอมมิต แต่สามารถอ่านการเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นหลังจากการคอมมิตได้
หากเรามีธุรกรรมที่ยาว T1 ในระหว่างที่มีการคอมมิตเกิดขึ้นในธุรกรรม T2, T3 ... Tn ซึ่งทำงานกับข้อมูลเดียวกันกับ T1 ดังนั้นเมื่อขอข้อมูลใน T1 เราจะได้รับผลลัพธ์ที่แตกต่างกันในแต่ละครั้ง ปรากฏการณ์นี้เรียกว่าการอ่านซ้ำไม่ได้
อ่านซ้ำได้ — ในระดับการแยกนี้ เราไม่มีปรากฏการณ์การอ่านซ้ำไม่ได้ เนื่องจากคำขอแต่ละรายการในการอ่านข้อมูล จะมีการสร้างสแน็ปช็อตของข้อมูลผลลัพธ์ และเมื่อนำมาใช้ซ้ำในธุรกรรมเดียวกัน ข้อมูลจากสแน็ปช็อต ถูกนำมาใช้. อย่างไรก็ตาม คุณสามารถอ่านข้อมูล Phantom ในระดับการแยกนี้ได้ นี่หมายถึงการอ่านแถวใหม่ที่ถูกเพิ่มโดยธุรกรรมที่คอมมิตแบบขนาน
ซีเรียลไลซ์ได้ - ฉนวนระดับสูงสุด เป็นลักษณะความจริงที่ว่าข้อมูลที่ใช้ในธุรกรรม (การอ่านหรือการเปลี่ยนแปลง) จะพร้อมใช้งานสำหรับธุรกรรมอื่น ๆ หลังจากเสร็จสิ้นธุรกรรมครั้งแรกเท่านั้น
ขั้นแรก เรามาพิจารณาว่ามีการแยกการดำเนินการในธุรกรรมจากเธรดหลักหรือไม่ มาเปิดหน้าต่างเทอร์มินัล 2 อัน
Kill ^t
Write ^t(1)
2
TSTART
Set ^t(1)=2ไม่มีความโดดเดี่ยว เธรดหนึ่งจะดูว่าเธรดที่สองที่เปิดธุรกรรมกำลังทำอะไรอยู่
มาดูกันว่าธุรกรรมของเธรดที่แตกต่างกันดูว่าเกิดอะไรขึ้นภายในเธรดเหล่านั้นหรือไม่
มาเปิดหน้าต่างเทอร์มินัล 2 อันและเปิด 2 ธุรกรรมพร้อมกัน
kill ^t
TSTART
Write ^t(1)
3
TSTART
Set ^t(1)=3
การทำธุรกรรมแบบขนานจะเห็นข้อมูลของกันและกัน ดังนั้นเราจึงได้ระดับการแยกที่ง่ายที่สุด แต่ยังแยกได้เร็วที่สุดด้วย READ UNCOMMITED
โดยหลักการแล้ว สิ่งนี้สามารถคาดหวังได้สำหรับระดับโลก ซึ่งประสิทธิภาพถือเป็นสิ่งสำคัญมาโดยตลอด
จะเกิดอะไรขึ้นหากเราต้องการความโดดเดี่ยวในระดับที่สูงขึ้นในการดำเนินงานบน Globals?
ที่นี่คุณต้องพิจารณาว่าเหตุใดจึงต้องมีระดับการแยกกักกันและวิธีการทำงาน
ระดับการแยกสูงสุด SERIALIZE หมายความว่าผลลัพธ์ของธุรกรรมที่ดำเนินการแบบขนานนั้นเทียบเท่ากับการดำเนินการตามลำดับ ซึ่งรับประกันว่าจะไม่มีการชนกัน
เราสามารถทำได้โดยใช้การล็อคอัจฉริยะใน ObjectScript ซึ่งมีการใช้งานที่แตกต่างกันมากมาย: คุณสามารถทำการล็อคแบบปกติ แบบเพิ่มทีละหลายครั้งด้วยคำสั่ง .
ระดับการแยกที่ต่ำกว่าคือการแลกเปลี่ยนที่ออกแบบมาเพื่อเพิ่มความเร็วของฐานข้อมูล
มาดูกันว่าเราสามารถบรรลุระดับการแยกตัวโดยใช้ล็อคในระดับต่างๆ ได้อย่างไร
โอเปอเรเตอร์นี้ช่วยให้คุณไม่เพียงแต่ใช้การล็อกพิเศษที่จำเป็นในการเปลี่ยนแปลงข้อมูล แต่ยังเรียกว่าการล็อกแบบแบ่งใช้ ซึ่งสามารถรับเธรดหลายเธรดพร้อมกันเมื่อจำเป็นต้องอ่านข้อมูลที่ไม่ควรเปลี่ยนแปลงโดยกระบวนการอื่นในระหว่างกระบวนการอ่าน
ข้อมูลเพิ่มเติมเกี่ยวกับวิธีการบล็อกสองเฟสในภาษารัสเซียและอังกฤษ:
→
→
ปัญหาคือในระหว่างการทำธุรกรรม สถานะของฐานข้อมูลอาจไม่สอดคล้องกัน แต่ข้อมูลที่ไม่สอดคล้องกันนี้จะปรากฏแก่กระบวนการอื่น ๆ จะหลีกเลี่ยงสิ่งนี้ได้อย่างไร?
เมื่อใช้ล็อคเราจะสร้างหน้าต่างการมองเห็นซึ่งสถานะของฐานข้อมูลจะสอดคล้องกัน และการเข้าถึงหน้าต่างการมองเห็นของรัฐที่ตกลงไว้ทั้งหมดจะถูกควบคุมโดยล็อค
การล็อกที่ใช้ร่วมกันในข้อมูลเดียวกันสามารถนำกลับมาใช้ใหม่ได้ ซึ่งกระบวนการต่างๆ มากมายสามารถนำมาใช้ได้ การล็อคเหล่านี้ป้องกันไม่ให้กระบวนการอื่นเปลี่ยนแปลงข้อมูล เช่น ใช้เพื่อสร้างหน้าต่างสถานะฐานข้อมูลที่สอดคล้องกัน
การล็อคแบบพิเศษใช้สำหรับการเปลี่ยนแปลงข้อมูล - มีเพียงกระบวนการเดียวเท่านั้นที่สามารถทำการล็อคดังกล่าวได้ การล็อคแบบเอกสิทธิ์เฉพาะบุคคลสามารถทำได้โดย:
- กระบวนการใดๆ หากข้อมูลว่าง
- เฉพาะกระบวนการที่มีการล็อกที่ใช้ร่วมกันกับข้อมูลนี้และเป็นกระบวนการแรกที่ร้องขอการล็อกแบบเอกสิทธิ์เฉพาะบุคคล

ยิ่งหน้าต่างการมองเห็นแคบลง กระบวนการอื่นๆ ก็ยิ่งต้องรอนานขึ้นเท่านั้น แต่สถานะของฐานข้อมูลภายในก็จะมีความสอดคล้องกันมากขึ้นเท่านั้น
READ_COMMITTED — สาระสำคัญของระดับนี้คือเราจะเห็นเฉพาะข้อมูลที่คอมมิตจากเธรดอื่นเท่านั้น หากยังไม่มีการยืนยันข้อมูลในธุรกรรมอื่น เราจะเห็นเวอร์ชันเก่า
ซึ่งจะทำให้เราสามารถทำงานแบบขนานแทนการรอให้ปลดล็อคได้
หากไม่มีเทคนิคพิเศษ เราจะไม่สามารถดูข้อมูลเวอร์ชันเก่าใน IRIS ได้ ดังนั้นเราจะต้องทำการล็อคต่อไป
ดังนั้น เราจะต้องใช้การล็อกที่ใช้ร่วมกันเพื่อให้ข้อมูลสามารถอ่านได้เฉพาะในช่วงเวลาที่มีความสอดคล้องกันเท่านั้น
สมมติว่าเรามีฐานผู้ใช้^คนโอนเงินให้กัน
ช่วงเวลาแห่งการโอนจากบุคคล 123 ไปยังบุคคล 242:
LOCK +^person(123), +^person(242)
Set ^person(123, amount) = ^person(123, amount) - amount
Set ^person(242, amount) = ^person(242, amount) + amount
LOCK -^person(123), -^person(242)ช่วงเวลาขอเงินจากบุคคล 123 ก่อนการหักบัญชีจะต้องมาพร้อมกับบล็อกพิเศษ (โดยค่าเริ่มต้น):
LOCK +^person(123)
Write ^person(123)และหากคุณต้องการแสดงสถานะบัญชีในบัญชีส่วนตัวของคุณ คุณสามารถใช้ล็อคที่ใช้ร่วมกันหรือไม่ใช้เลยก็ได้:
LOCK +^person(123)#”S”
Write ^person(123)อย่างไรก็ตาม หากเราถือว่าการดำเนินการฐานข้อมูลดำเนินการเกือบจะในทันที (ฉันขอเตือนคุณว่า globals เป็นโครงสร้างระดับที่ต่ำกว่าตารางเชิงสัมพันธ์มาก) ความจำเป็นสำหรับระดับนี้จะลดลง
อ่านซ้ำได้ - ระดับการแยกนี้ช่วยให้สามารถอ่านข้อมูลได้หลายครั้งซึ่งสามารถแก้ไขได้โดยธุรกรรมที่เกิดขึ้นพร้อมกัน
ดังนั้น เราจะต้องล็อกที่ใช้ร่วมกันในการอ่านข้อมูลที่เราเปลี่ยนแปลง และล็อกเฉพาะกับข้อมูลที่เราเปลี่ยนแปลง
โชคดีที่ตัวดำเนินการ LOCK ช่วยให้คุณสามารถแสดงรายการรายละเอียดการล็อคที่จำเป็นทั้งหมดซึ่งอาจมีจำนวนมากได้ในคำสั่งเดียว
LOCK +^person(123, amount)#”S”
чтение ^person(123, amount)การดำเนินการอื่นๆ (ในขณะนี้ เธรดแบบขนานพยายามเปลี่ยน ^person(123, จำนวน) แต่ทำไม่ได้)
LOCK +^person(123, amount)
изменение ^person(123, amount)
LOCK -^person(123, amount)
чтение ^person(123, amount)
LOCK -^person(123, amount)#”S”เมื่อแสดงรายการล็อคโดยคั่นด้วยเครื่องหมายจุลภาค ระบบจะดำเนินการตามลำดับ แต่ถ้าคุณทำสิ่งนี้:
LOCK +(^person(123),^person(242))จากนั้นพวกมันก็จะถูกถ่ายแบบอะตอมทั้งหมดในคราวเดียว
ซีเรียลไลซ์ — เราจะต้องตั้งค่าการล็อคเพื่อให้ธุรกรรมทั้งหมดที่มีข้อมูลทั่วไปได้รับการดำเนินการตามลำดับในที่สุด สำหรับแนวทางนี้ การล็อคส่วนใหญ่ควรเป็นแบบเอกสิทธิ์เฉพาะบุคคลและดำเนินการในพื้นที่ที่เล็กที่สุดในโลกเพื่อประสิทธิภาพ
หากเราพูดถึงการหักเงินใน ^person ทั่วโลก จะยอมรับได้เฉพาะระดับการแยก SERIALIZE เท่านั้น เนื่องจากต้องใช้เงินตามลำดับอย่างเคร่งครัด มิฉะนั้น อาจเป็นไปได้ที่จะใช้จ่ายจำนวนเท่ากันหลายครั้ง
4. ความทนทาน
ฉันทำการทดสอบด้วยการตัดภาชนะอย่างแรงโดยใช้
docker kill my-irisฐานทนพวกเขาได้ดี ไม่พบปัญหาใดๆ
ข้อสรุป
สำหรับระดับโลก InterSystems IRIS รองรับธุรกรรม พวกมันมีอะตอมมิกและเชื่อถือได้อย่างแท้จริง เพื่อให้มั่นใจถึงความสอดคล้องของฐานข้อมูลตาม globals จำเป็นต้องมีความพยายามของโปรแกรมเมอร์และการใช้ธุรกรรม เนื่องจากไม่มีโครงสร้างในตัวที่ซับซ้อน เช่น คีย์ต่างประเทศ
ระดับการแยกของ globals โดยไม่ใช้ล็อคคือ READ UNCOMMITED และเมื่อใช้ล็อค จะสามารถรับประกันได้ถึงระดับ SERIALIZE
ความถูกต้องและความเร็วของการทำธุรกรรมบน globals นั้นขึ้นอยู่กับทักษะของโปรแกรมเมอร์เป็นอย่างมาก: ยิ่งมีการใช้ล็อคที่ใช้ร่วมกันอย่างกว้างขวางมากขึ้นเมื่ออ่าน ระดับการแยกตัวก็จะยิ่งสูงขึ้น และยิ่งทำการล็อคแบบพิเศษที่แคบมากขึ้นเท่านั้น ประสิทธิภาพก็จะเร็วขึ้นตามไปด้วย
ที่มา: will.com
