การติดตามการดีบักและการแก้ไขข้อขัดแย้งของ Row Lock


12

ดึกฉันได้รับการโต้เถียงกันมากเกี่ยวกับการล็อกแถว ดูเหมือนว่าตารางในการช่วงชิงจะเป็นตารางเฉพาะ

นี่คือสิ่งที่เกิดขึ้นโดยทั่วไป -

  • นักพัฒนา 1 เริ่มทำธุรกรรมจากหน้าจอส่วนหน้าของ Oracle Forms
  • นักพัฒนา 2 เริ่มทำธุรกรรมอื่นจากเซสชันที่ต่างกันโดยใช้หน้าจอเดียวกัน

~ 5 นาทีในส่วนหน้าดูเหมือนว่าไม่ตอบสนอง การตรวจสอบเซสชันแสดงการช่วงชิงล็อกแถว "ทางออก" ที่ทุกคนขว้างไปรอบ ๆ คือฆ่าช่วง: /

เป็นผู้พัฒนาฐานข้อมูล

  • สิ่งใดที่สามารถทำได้เพื่อกำจัดข้อขัดแย้งในการล็อกแถว
  • เป็นไปได้ไหมที่จะพบว่าบรรทัดของโพรซีเดอร์ที่เก็บไว้ก่อให้เกิดข้อขัดแย้งในการล็อกแถวเหล่านี้หรือไม่
  • อะไรคือแนวทางทั่วไปในการลด / หลีกเลี่ยง / กำจัดปัญหาดังกล่าวซึ่งเป็นรหัส?

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


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


เวอร์ชันฐานข้อมูลคือ Oracle Database 10g Enterprise Edition รีลีส 10.2.0.1.0 - 64 บิต ลำดับของลอจิกถูกดำเนินการตามลำดับเดียวกันในทั้งสองเซสชันธุรกรรมไม่ได้เปิดไว้นานเกินไป (หรืออย่างน้อยฉันก็คิดอย่างนั้น) และการล็อคเกิดขึ้นในระหว่างการดำเนินการธุรกรรม


อัปเดต:จำนวนแถวของตารางมีขนาดใหญ่กว่าที่คาดไว้ที่ประมาณ 3.1 ล้านแถว นอกจากนี้หลังจากติดตามเซสชันฉันพบว่าคำสั่ง update สองสามรายการในตารางนี้ไม่ได้ใช้ดัชนี ทำไมถึงเป็นเช่นนั้น - ฉันไม่แน่ใจ คอลัมน์ที่อ้างอิงในส่วนคำสั่งถูกทำดัชนี ฉันกำลังสร้างดัชนีใหม่


1
@Satya - คุณสามารถอธิบายความซับซ้อนของขั้นตอนการจัดเก็บได้อย่างไร ตารางที่สงสัยว่าอยู่ระหว่างการอัพเดทหรือการแทรกอย่างเข้มงวด?
CoderHawk

คีย์ต่างประเทศมีบทบาทที่นี่หรือไม่ (บางครั้งต้องมีดัชนี) มีฐานข้อมูลรุ่นใดอยู่ การไหลของตรรกะดำเนินการในลำดับเดียวกันในทั้งสองเซสชันหรือไม่? ธุรกรรมนั้น 'เปิด' เป็นเวลานานหรือไม่? การล็อกเกิดขึ้นระหว่างที่ผู้ใช้คิดเวลาหรือระหว่างการดำเนินการธุรกรรม
ik_zelf

@Sandy ฉันได้อัปเดตคำถาม
Sathyajith Bhat

@ik_zelf ฉันได้อัปเดตคำถามแล้ว
Sathyajith Bhat

1
ยังไม่ชัดเจนสำหรับฉันว่าทำไมถึงมีปัญหา - Oracle กำลังทำสิ่งที่ควรทำอย่างแน่นอนซึ่งเป็นการซีเรียลไลเซชันการเข้าถึงแถวเดียว หากใครบางคนมีแถวนั้นคุณสามารถอ่านเวอร์ชันก่อนหน้าได้ แต่การเขียนคุณต้องรอให้พวกเขาปลดล็อค "แก้ไข" เพียงอย่างเดียวสำหรับมันคือการก) ไม่หลอกและCOMMITหรือROLLBACKในเวลาที่เหมาะสมหรือข) จัดให้ผู้คนเดียวกันไม่ต้องการแถวเดียวกันในเวลาเดียวกัน
ออกุสตุส

คำตอบ:


10

เป็นไปได้ไหมที่จะพบว่าบรรทัดของโพรซีเดอร์ที่เก็บไว้ใดเป็นสาเหตุของการโต้แย้งแถวล็อคเหล่านี้หรือไม่?

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

SELECT sid, sql_text
FROM v$session s
LEFT JOIN v$sql q ON q.sql_id=s.sql_id
WHERE state = 'WAITING' AND wait_class != 'Idle'
AND event = 'enq: TX - row lock contention';

อะไรคือแนวทางทั่วไปในการลด / หลีกเลี่ยง / กำจัดปัญหาดังกล่าวด้วยการเข้ารหัส?

ส่วนOracle Concepts Guide บนตัวล็อคระบุว่า "แถวถูกล็อคเฉพาะเมื่อแก้ไขโดยผู้เขียน" เซสชั่นอื่นการอัปเดตแถวเดียวกันจะรอให้เซสชั่นแรกเป็นCOMMITหรือROLLBACKก่อนที่จะสามารถดำเนินการต่อ เพื่อกำจัดปัญหาที่คุณสามารถทำให้เป็นอันดับผู้ใช้ แต่นี่คือสิ่งที่สามารถลดปัญหาที่อาจจะถึงระดับของมันไม่เป็นปัญหา

  • COMMITบ่อยขึ้น. ทุกCOMMITๆ รีลีสล็อคดังนั้นถ้าคุณสามารถทำการอัพเดตแบบแบตช์โอกาสของเซสชันอื่นที่ต้องการแถวเดียวกันจะลดลง
  • ตรวจสอบให้แน่ใจว่าคุณไม่ได้อัปเดตแถวใด ๆ โดยไม่เปลี่ยนค่า ยกตัวอย่างเช่นUPDATE t1 SET f1=DECODE(f2,’a’,f1+1,f1);ควรจะเขียนใหม่เป็นเลือกมากขึ้น UPDATE t1 SET f1=f1+1 WHERE f2=’a’;(อ่านล็อคน้อยกว่า) แน่นอนหากการเปลี่ยนแปลงคำสั่งจะยังคงล็อคส่วนใหญ่ของแถวในตารางแล้วการเปลี่ยนแปลงจะมีประโยชน์ในการอ่านเท่านั้น
  • ตรวจสอบให้แน่ใจว่าคุณกำลังใช้ลำดับแทนที่จะล็อกตารางเพื่อเพิ่มหนึ่งค่าที่เป็นค่าสูงสุดในปัจจุบัน
  • ตรวจสอบให้แน่ใจว่าคุณไม่ได้ใช้ฟังก์ชั่นที่ทำให้ดัชนีไม่สามารถใช้งานได้ หากจำเป็นต้องใช้ฟังก์ชั่นให้พิจารณาทำให้เป็นดัชนีที่อิงกับฟังก์ชัน
  • คิดเป็นชุด พิจารณาว่าการวนซ้ำที่เรียกใช้บล็อกของ PL / SQL ที่ทำการอัพเดตสามารถเขียนใหม่เป็นคำสั่งอัพเดตเดียวได้หรือไม่ BULK COLLECT ... FORALLถ้าไม่ได้แล้วบางทีการประมวลผลจำนวนมากสามารถนำมาใช้กับ
  • ลดการทำงานที่ได้รับทำระหว่างครั้งแรกและUPDATE COMMITตัวอย่างเช่นหากรหัสส่งอีเมลหลังการอัปเดตแต่ละครั้งให้พิจารณาจัดคิวอีเมลและส่งพวกเขาหลังจากยอมรับการอัปเดต
  • การออกแบบโปรแกรมประยุกต์เพื่อการจัดการการรอคอยด้วยการทำหรือSELECT ... FOR UPDATE NOWAIT WAIT 2จากนั้นคุณสามารถตรวจจับการไม่สามารถล็อคแถวและแจ้งให้ผู้ใช้ทราบว่าเซสชันอื่นกำลังแก้ไขข้อมูลเดียวกัน

7

ฉันจะให้คำตอบจากมุมมองของนักพัฒนา

ในความคิดของฉันเมื่อคุณพบข้อโต้แย้งแถวเช่นสิ่งที่คุณอธิบายมันเป็นเพราะคุณมีข้อบกพร่องในใบสมัครของคุณ ในกรณีส่วนใหญ่การช่วงชิงประเภทนี้เป็นสัญญาณของความเสี่ยงที่เกิดจากการอัพเดท หัวข้อนี้ใน AskTomอธิบายแนวคิดของการอัพเดทที่หายไป:

การอัปเดตที่หายไปจะเกิดขึ้นเมื่อ:

เซสชัน 1: อ่านบันทึกการทำงานของทอม

เซสชัน 2: อ่านบันทึกการทำงานของทอม

เซสชัน 1: อัปเดตข้อมูลพนักงานของทอม

ช่วงที่ 2: อัพเดทข้อมูลพนักงานของ Tom

ช่วงที่ 2 จะมีการเปลี่ยนแปลงช่วงที่ 1 ของ WRITE โดยที่ไม่เคยเห็นทำให้เกิดการอัปเดตที่หายไป

คุณประสบกับผลข้างเคียงที่น่ารังเกียจของการอัพเดทที่หายไป: ช่วงที่ 2 สามารถบล็อกได้เนื่องจากช่วงที่ 1 ยังไม่ได้ส่งคำสั่ง อย่างไรก็ตามปัญหาหลักคือเซสชันที่ 2 จะอัพเดตเร็กคอร์ดอย่างสุ่ม สมมติว่าทั้งสองเซสชันออกคำสั่ง:

UPDATE table SET col1=:col1, ..., coln=:coln WHERE id = :pk

หลังจากทั้งสองประโยคการแก้ไขของเซสชัน 1 ถูกเขียนทับโดยไม่มีเซสชัน 2 ที่ได้รับแจ้งว่ามีการแก้ไขแถวโดยเซสชัน 1


การอัปเดตที่หายไป (และผลข้างเคียงของการโต้แย้ง) ไม่ควรเกิดขึ้นมันหลีกเลี่ยงได้ 100% คุณควรใช้ล็อคเพื่อป้องกันไม่ให้พวกเขาด้วยสองวิธีหลัก: ในแง่ดีและแง่ร้ายล็อค

1) การล็อคในแง่ร้าย

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

2) การล็อคในแง่ดี

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

SELECT <...>
  FROM table
 WHERE id = :id
   AND marker = :marker
   FOR UPDATE NOWAIT

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

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

UPDATE table
   SET <...>, 
       marker = marker + 1
 WHERE id = :id;

หากคำสั่งไม่มีการอัพเดตแถวคุณรู้ว่ามีคนเปลี่ยนแถวนี้และคุณต้องเริ่มต้นใหม่ทั้งหมด

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

TL; DR

  • การอัปเดตแถวโดยไม่ต้องล็อคก่อนจะทำให้แอปพลิเคชันมีโอกาส "ค้าง" สิ่งนี้สามารถหลีกเลี่ยงได้หาก DML ทั้งหมดไปยัง DB ใช้การล็อกในแง่ดีหรือมองโลกในแง่ร้าย
  • ตรวจสอบว่าคำสั่ง SELECT ส่งคืนค่าที่สอดคล้องกับ SELECT ก่อนหน้าใด ๆ (เพื่อหลีกเลี่ยงปัญหาการอัพเดทที่สูญหาย)

5

คำตอบนี้อาจมีคุณสมบัติเหมาะสมสำหรับการเข้าร่วมใน The Daily WTF

ใช่หลังจากติดตามเซสชันและค้นหาผ่านUSER_SOURCE- ฉันติดตามสาเหตุที่แท้จริง

  • สาเหตุไม่แปลกใจคือตรรกะที่มีข้อบกพร่อง
  • เมื่อเร็ว ๆ นี้คำสั่งการปรับปรุงถูกเพิ่มเข้าไปใน SP คำสั่ง update โดยทั่วไปจะปรับปรุงตารางทั้งหมด เห็นได้ชัดว่านักพัฒนาที่มีปัญหาลืมเกี่ยวกับการเพิ่มสิทธิ์ในการที่จะปรับปรุงคำสั่งที่จำเป็น
  • ตารางที่ได้รับการปรับปรุงดังกล่าวข้างต้นซึ่งเป็นหนึ่งในตารางที่ทำธุรกรรมมากที่สุดและมีระเบียนจำนวนมาก การอัปเดตอาจใช้เวลานานและเจ็บปวดใจ
  • ผลลัพธ์คือเซสชันอื่นไม่สามารถรับการล็อกบนโต๊ะและจะนั่งในการช่วงชิงล็อกแถว
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.