ฉันเคยได้ยินปัญหาการทำงานพร้อมกันเช่นนั้นใน 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ตารางนั่นไม่ควรเกิดขึ้น