MySQL: ธุรกรรมจะล็อกแถวหรือไม่


13

ฉันไม่เคยลองใช้ธุรกรรม MySQL มาก่อนฉันแค่ต้องการชี้แจงบางอย่าง

หากผู้ใช้สองคนเรียกใช้แบบสอบถามในเวลาที่แน่นอน MySQL จะจัดการเรื่องนี้อย่างไร เช่นผู้ใช้พยายามอัปเดตบันทึก

user1: อัปเดตชุดตารางคอลัมน์ = คอลัมน์ - 4 โดยที่ column_id = 1;

user2: อัปเดตชุดคอลัมน์ตาราง = คอลัมน์ - 7 โดยที่ column_id = 1;

ตอนนี้ถ้าฉันใช้ทรานแซกชัน MySQL จะเลือกแบบสอบถามที่จะถูกดำเนินการก่อนและล็อกผู้ใช้รายที่สองจนกว่าจะมีการสืบค้นครั้งแรก จะเป็นล็อคตารางหรือล็อคแถวหรือไม่?

จะเป็นอย่างไรถ้าผู้ใช้รายที่สามออกคำสั่ง select? MySQL จะคืนค่าอะไร?

ป.ล. นี้จะอยู่ที่ Innodb

คำตอบ:


17

คำสั่งเดียวเช่นนั้นที่ทำงานได้เหมือนกันกับ MyISAM หรือ InnoDB กับธุรกรรมหรือกับ autocommit = ON มันบล็อกเพียงพอที่จะทำแบบสอบถามจึงบล็อกการเชื่อมต่ออื่น ๆ เมื่อเสร็จสิ้นการเชื่อมต่ออื่น ๆ จะดำเนินต่อไป ในทุกกรณีคอลัมน์จะลดลงทันที 11

ผู้ใช้รายที่สามอาจเห็นค่าที่ลดลงโดย 0 หรือ 4 หรือ 7 หรือ 11 "เวลาที่แน่นอนมาก" เป็นไปไม่ได้เพราะในบางครั้งในการดำเนินการของแต่ละคำสั่งล็อคแบบเธรดเดียวจะถูกตรวจสอบ / ตั้งค่า / อะไรก็ตาม . นั่นคือพวกเขาจะได้รับการต่อเนื่องอย่างรวดเร็วจนคุณมองไม่เห็น

InnoDB ล็อคเฉพาะแถวไม่ใช่ตาราง (ตกลงคำสั่ง DDL ทำการล็อคที่โดดเด่นยิ่งขึ้น)

สิ่งที่น่าสนใจยิ่งกว่าคือธุรกรรมที่ปรับเปลี่ยนสองสิ่งหรือใช้เวลาในการสังเกต:

กรณีความตั้งใจ:รายการเดียว แต่สละเวลา:

BEGIN;
SELECT something;
think about it for a while
UPDATE that something;
COMMIT;

การเลือกจะต้องมีการเขียนดังนี้:

SELECT something  FOR UPDATE;

สิ่งนี้จะบอกการเชื่อมต่ออื่น ๆ "ฉันต้องการอัพเดทแถวโปรดอย่าทำให้ฉันสับสน" (ฉันขอยกตัวอย่างนี้เพราะมือใหม่จำนวนมากพลาดความฉลาดนี้)

กรณีการหยุดชะงัก: ล้อเล่นกับ 2 สิ่ง:

BEGIN;    -- in one connection
UPDATE thing_1;
UPDATE thing_2;
COMMIT;

BEGIN;    -- in another connection, at the "exact same time"
UPDATE thing_2;
UPDATE thing_1;
COMMIT;

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

ปฏิกิริยาปกติต่อการหยุดชะงักคือการเล่นซ้ำธุรกรรมทั้งหมดที่ล้มเหลว จากนั้นการเชื่อมต่ออื่น ๆ จะไม่รบกวนและควรดำเนินการต่อโดยไม่มีปัญหา (ตกลงการเชื่อมต่ออื่นสามารถสร้างการหยุดชะงักได้อีก)

กรณีที่ล่าช้า:หากการเชื่อมต่อทั้งสองหยิบหลายสิ่งในลำดับเดียวกันการเชื่อมต่อหนึ่งอาจล่าช้าจนกว่าการเชื่อมต่ออื่น ๆ จะเสร็จสิ้น เพื่อให้นี้จาก "รอตลอดไป" มีการเริ่มต้น innodb_lock_wait_timeout50 คู่ที่เรียบง่ายของคุณUPDATEsเป็นตัวอย่างของกรณีนี้จริง ๆ หนึ่งจะจบทันที อีกจนจนครบเป็นครั้งแรก

สังเกตว่า Deadlock สามารถเปลี่ยนเป็น Delay ได้อย่างไรโดยการสั่งซื้อสิ่งที่คุณสัมผัสอย่างสม่ำเสมอ

autocommit = 1: ด้วยการตั้งค่านี้และไม่มีการเรียกBEGINแต่ละคำสั่งจะมีประสิทธิภาพ:

BEGIN;
your statement
COMMIT;

autocommit = 0: นี่เป็นปัญหาที่รอให้เกิดขึ้น เมื่อคุณดำเนินการแบบสอบถามการเขียนBEGINจะถูกสร้างขึ้นโดยปริยาย COMMITแต่ก็เป็นความรับผิดชอบของคุณกับปัญหาในที่สุด หากคุณไม่ทำเช่นนั้นคุณจะสงสัยว่าทำไมระบบของคุณถึงหยุดทำงาน (ข้อผิดพลาด newbie ทั่วไปอื่น ๆ ) คำแนะนำของฉัน: "ไม่เคยใช้=0"

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