order_parcels
ใช้ในเคส 1–3
idBIGINT UNSIGNED AUTO_INCREMENT
order_idVARCHAR(64)
order_parcel_numberVARCHAR(64)
status, amount, …—
INDEX order_parcels_order_id_IDX (order_id)
⭐ UNIQUE INDEX (order_id, order_parcel_number)
ก่อนแก้
✕ ไม่มี unique index ตัวนี้
แถวซ้ำ order_id+parcel_number หลุดเข้าไปได้
หลังแก้
✓ เพิ่ม unique index ตัวนี้
ODKU upsert อาศัย constraint นี้ตัดสิน "ซ้ำ"
ไม่มี unique index ⇒ โค้ดเดิมต้องเดา ทำ UPDATE ก่อนแล้วเช็คว่า 0 rows ค่อย INSERT เอง — ช่องว่างระหว่างสองคำสั่งนี้คือต้นตอ deadlock ของเคส 1–3 ทั้งหมด
ALTER TABLE order_parcels
ADD UNIQUE INDEX idx_order_parcels_order_id_parcel_number
(order_id, order_parcel_number);
order_parcel_items
ใช้ในเคส 4–5
order_idVARCHAR(64)
order_parcel_idBIGINT UNSIGNED
order_item_idVARCHAR(64)
qty, insurances, …—
✓ PRIMARY KEY (order_id, order_parcel_id, order_item_id)
⚠ INDEX (order_parcel_id) — non-unique, ไม่ใช่ FK ทั้งชุด
schema ไม่เปลี่ยนเลย
PK เดิมก็ unique อยู่แล้ว ไม่ต้องเพิ่ม index ใหม่
ปัญหาอยู่ที่ "query ไหนถูกยิง" ไม่ใช่ตัว schema
query เดิมยิง DELETE WHERE order_parcel_id=? ซึ่งเดินผ่าน index ที่ไม่ unique ตัวนี้ — แม้ไม่ match แถวไหนเลยก็ยังโดน gap lock ทางแก้คือเปลี่ยนวิธี query (DeleteStale) ให้ลบผ่าน PK เต็มตรงๆ แทน ไม่ใช่แก้ schema
-- ก่อนแก้ (ยิงผ่าน non-unique index, กิน gap):
DELETE FROM order_parcel_items WHERE order_parcel_id = ?;
-- หลังแก้ (ยิงผ่าน PK เต็ม, ล็อกเป็นจุด ไม่มี gap):
DELETE FROM order_parcel_items
WHERE order_id = ? AND order_parcel_id = ? AND order_item_id IN (...);