การจัดการภาวะพร้อมกันเมื่อใช้รูปแบบ SELECT-UPDATE


25

สมมติว่าคุณมีรหัสต่อไปนี้ (โปรดอย่าสนใจว่ามันแย่มาก):

BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else

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

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

ฉันค่อนข้างแน่ใจว่าการเพิ่มคำWITH (UPDLOCK, HOLDLOCK)ใน SELECT จะทำเคล็ดลับ ระดับ SERIALIZABLE แยกธุรกรรมจะดูเหมือนว่าจะทำงานได้เป็นอย่างดีเพราะมันปฏิเสธคนอื่นที่จะอ่านสิ่งที่คุณทำจน tran ที่มีมากกว่า ( UPDATE : นี้เป็นเท็จคำตอบที่ดูมาร์ติน.) มันเป็นเรื่องจริงเหรอ? พวกเขาทั้งสองจะทำงานได้ดีเท่าเทียมกันหรือไม่ เป็นที่ต้องการมากกว่าหนึ่งอื่น ๆ ?

ลองนึกภาพการทำสิ่งที่ถูกกฎหมายมากกว่าการอัปเดต ID - การคำนวณบางอย่างจากการอ่านที่คุณต้องอัปเดต อาจมีหลายตารางที่เกี่ยวข้องซึ่งบางส่วนที่คุณจะเขียนและอื่น ๆ ที่คุณจะไม่ การปฏิบัติที่ดีที่สุดที่นี่คืออะไร?

เมื่อเขียนคำถามนี้ฉันคิดว่าคำแนะนำการล็อคดีกว่าเพราะคุณเพียงล็อคตารางที่คุณต้องการ แต่ฉันขอขอบคุณที่ทุกคนป้อนข้อมูล

ป.ล. และไม่ฉันไม่ทราบคำตอบที่ดีที่สุดและต้องการได้รับความเข้าใจที่ดีขึ้น! :)


เพื่อความกระจ่าง: คุณต้องการป้องกันไม่ให้ลูกค้า 2 รายอ่านค่าเดียวกันหรือออกจากการใช้งานupdateซึ่งอาจเป็นไปตามข้อมูลที่ล้าสมัยหรือไม่? หากภายหลังคุณสามารถใช้rowversionคอลัมน์เพื่อตรวจสอบว่าแถวที่จะอัปเดตไม่ได้เปลี่ยนไปตั้งแต่อ่านแล้วหรือไม่
a1ex07

เราไม่ต้องการให้ลูกค้ารายที่สองรับค่ารหัสเก่าก่อนที่จะได้รับการอัปเดตเป็นค่าใหม่โดยลูกค้ารายแรก มันควรปิดกั้น
ErikE

คำตอบ:


11

เพียงระบุที่อยู่ในSERIALIZABLEระดับการแยก ใช่สิ่งนี้จะใช้ได้ แต่มีความเสี่ยงการหยุดชะงัก

ธุรกรรมทั้งสองจะสามารถอ่านแถวพร้อมกันได้ พวกเขาจะไม่ปิดกั้นแต่ละอื่น ๆ ที่พวกเขาทั้งสองจะใช้วัตถุSล็อคหรือดัชนีRangeS-Sล็อคขึ้นอยู่กับโครงสร้างของตารางและล็อคเหล่านี้จะเข้ากันได้ แต่พวกเขาจะบล็อกซึ่งกันและกันเมื่อพยายามที่จะได้รับการล็อคที่จำเป็นสำหรับการปรับปรุง ( IXล็อควัตถุหรือดัชนีRangeS-Uตามลำดับ) ซึ่งจะนำไปสู่การหยุดชะงัก

การใช้UPDLOCKคำใบ้อย่างชัดเจนแทนจะทำให้เป็นอันดับอ่านดังนั้นหลีกเลี่ยงความเสี่ยงการหยุดชะงัก


+1 แต่: สำหรับตารางฮีปคุณยังสามารถรับการหยุดชะงักการแปลงแม้จะมีการล็อคการอัปเดต: sqlblog.com/blogs/alexander_kuznetsov/archive/2009/03/11/…
AK

แปลกประหลาด @alex ฉันคิดว่ามันเกี่ยวข้องกับสภาพการแข่งขันของเครื่องยนต์ที่พยายามค้นหาสิ่งที่ต้องล็อคก่อนที่จะอัปเดตจริง ๆ ...
ErikE

@ErikE - มีการหยุดชะงักการแปลงในบทความของอเล็กซ์คือการแปลงจากIXไปXในกองของตัวเอง น่าสนใจไม่มีแถวใดที่มีคุณสมบัติเพียงพอดังนั้นจึงไม่มีการล็อคแถวออก ไม่แน่ใจว่าทำไมมันถึงXล็อคเลย
Martin Smith

11

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

ฉันได้เขียนหลายตัวอย่างของการทดสอบความเครียดที่นี่

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

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


9

ในกรณีพิเศษนี้การเพิ่มการUPDLOCKล็อคเพื่อSELECTป้องกันความผิดปกติอย่างแน่นอน นอกจากนี้HOLDLOCKไม่จำเป็นต้องเป็นล็อคการปรับปรุงจะจัดขึ้นในช่วงระยะเวลาของการทำธุรกรรม แต่ผมสารภาพกับมันรวมทั้งตัวเองเป็น (ที่ไม่ดีอาจจะ) นิสัยในอดีตที่ผ่านมา

ลองนึกภาพการทำสิ่งที่ถูกต้องมากกว่าการอัปเดต ID การคำนวณบางอย่างจากการอ่านที่คุณต้องอัปเดต อาจมีหลายตารางที่เกี่ยวข้องซึ่งบางส่วนที่คุณจะเขียนและอื่น ๆ ที่คุณจะไม่ การปฏิบัติที่ดีที่สุดที่นี่คืออะไร?

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

  • การดึงระดับสต็อกแบบแบนด์ (<5, 10+, 50+, 100+) สำหรับผลิตภัณฑ์ในร้านค้าบนเว็บ = สกปรกอ่าน (ไม่ถูกต้องไม่สำคัญ)
  • การตรวจสอบและลดระดับสต็อคในการชำระเงินที่ร้านค้าบนเว็บนั้น = อ่านซ้ำได้ (เราต้องมีสต็อกก่อนที่เราจะขายเราจะต้องไม่จบลงด้วยระดับสต็อกติดลบ)
  • การย้ายเงินสดระหว่างบัญชีปัจจุบันและบัญชีออมทรัพย์ของฉันที่ธนาคาร = ต่อเนื่องได้ (อย่าคาดคะเนหรือคำนวณเงินของฉันผิด!)

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

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