ฉันเคยได้ยินปัญหาการทำงานพร้อมกันเช่นนั้นใน MySQL มาก่อน ไม่เช่นนั้นใน Postgres
การล็อคระดับแถวในตัวในREAD COMMITTED
ระดับแยกธุรกรรมเริ่มต้นก็เพียงพอแล้ว
ฉันขอแนะนำคำสั่งเดียวที่มีการแก้ไขข้อมูล CTE (สิ่งที่ MySQL ยังไม่มี) เพราะสะดวกในการส่งผ่านค่าจากตารางหนึ่งไปยังอีกโดยตรง (ถ้าคุณต้องการ) หากคุณไม่ต้องการอะไรจากcoupon
ตารางคุณสามารถใช้ธุรกรรมที่มีการแยกUPDATE
และINSERT
งบได้เช่นกัน
WITH upd AS (
UPDATE coupon
SET used = true
WHERE coupon_id = 123
AND NOT used
RETURNING coupon_id, other_column
)
INSERT INTO log (coupon_id, other_column)
SELECT coupon_id, other_column FROM upd;
ควรเป็นเรื่องยากที่มีมากกว่าหนึ่งธุรกรรมที่พยายามแลกคูปองเดียวกัน พวกเขามีหมายเลขที่ไม่ซ้ำกันใช่มั้ย มากกว่าหนึ่งธุรกรรมที่พยายามในเวลาเดียวกันควรจะหายากกว่ามาก (อาจเป็นข้อบกพร่องของแอปพลิเคชันหรือมีคนพยายามเล่นเกมระบบ)
เป็นไปตามที่อาจUPDATE
ทำได้สำเร็จเพียงธุรกรรมเดียวเท่านั้นไม่ว่าจะเกิดอะไรขึ้น การUPDATE
รับล็อคระดับแถวในแต่ละแถวเป้าหมายก่อนอัปเดต หากการทำธุรกรรมพร้อมกันพยายามที่จะUPDATE
แถวเดียวกันมันจะเห็นล็อคในแถวและรอจนกว่าการปิดกั้นการทำธุรกรรมเสร็จสิ้น ( ROLLBACK
หรือCOMMIT
) จากนั้นเป็นคนแรกในคิวล็อค:
หากมุ่งมั่นให้ตรวจสอบสภาพอีกครั้ง หากยังคงNOT used
ล็อคแถวและดำเนินการต่อ อื่นUPDATE
ขณะนี้พบว่าไม่มีแถวที่มีคุณสมบัติและไม่มีอะไรกลับไม่มีแถวดังนั้นINSERT
ยังไม่ทำอะไรเลย
หากย้อนกลับให้ล็อคแถวและดำเนินการต่อ
มีศักยภาพในการแย่งไม่มี
ไม่มีความเป็นไปได้สำหรับการหยุดชะงักหากคุณไม่ได้ใส่การเขียนลงในธุรกรรมเดียวกันหรือล็อกแถวมากกว่าที่จะเขียน
ไม่INSERT
ต้องห่วง หากมีข้อผิดพลาดcoupon_id
อยู่แล้วในlog
ตาราง (และคุณมีข้อ จำกัด UNIQUE หรือ PK log.coupon_id
) ธุรกรรมทั้งหมดจะถูกย้อนกลับหลังจากการละเมิดที่ไม่ซ้ำกัน จะระบุสถานะที่ผิดกฎหมายในฐานข้อมูลของคุณ หากข้อความข้างต้นเป็นวิธีเดียวที่จะเขียนลงในlog
ตารางนั่นไม่ควรเกิดขึ้น