Saya pernah mendengar masalah konkurensi seperti itu di MySQL sebelumnya. Tidak demikian halnya dengan Postgres.
Kunci tingkat baris bawaan pada READ COMMITTED
tingkat isolasi transaksi default sudah cukup.
Saya menyarankan satu pernyataan dengan CTE pemodifikasi data (sesuatu yang MySQL juga tidak miliki) karena lebih mudah untuk menyampaikan nilai dari satu tabel ke tabel lainnya secara langsung (jika Anda memerlukannya). Jika Anda tidak memerlukan apa pun dari coupon
tabel, Anda dapat menggunakan transaksi dengan laporan terpisah UPDATE
dan INSERT
juga.
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;
Seharusnya menjadi hal yang langka bahwa lebih dari satu transaksi mencoba menebus kupon yang sama. Mereka memiliki nomor unik, bukan? Lebih dari satu transaksi yang mencoba pada saat yang sama harus jauh lebih jarang. (Mungkin bug aplikasi atau seseorang mencoba permainan sistem?)
Bagaimanapun , UPDATE
satu - satunya berhasil untuk tepat satu transaksi, apa pun yang terjadi. Seorang UPDATE
memperoleh kunci level baris pada setiap baris target sebelum memperbarui. Jika transaksi bersamaan mencoba ke UPDATE
baris yang sama, itu akan melihat kunci di baris dan menunggu sampai transaksi pemblokiran selesai ( ROLLBACK
atau COMMIT
), kemudian menjadi yang pertama dalam antrian kunci:
Jika berkomitmen, periksa kembali kondisinya. Jika masih NOT used
, kunci baris dan lanjutkan. Kalau tidak, UPDATE
sekarang tidak menemukan baris kualifikasi dan tidak melakukan apa pun , tidak mengembalikan baris, sehingga INSERT
juga tidak melakukan apa pun.
Jika dibatalkan, kunci baris dan lanjutkan.
Tidak ada potensi untuk kondisi balapan .
Tidak ada potensi kebuntuan kecuali Anda lebih banyak menulis ke dalam transaksi yang sama atau mengunci lebih banyak baris daripada hanya satu.
Ini INSERT
bebas perawatan. Jika, karena suatu kesalahan coupon_id
sudah ada dalam log
tabel (dan Anda memiliki batasan UNIK atau PK log.coupon_id
), seluruh transaksi akan dibatalkan setelah pelanggaran unik. Menunjukkan keadaan ilegal di DB Anda. Jika pernyataan di atas adalah satu-satunya cara untuk menulis ke log
tabel, itu seharusnya tidak pernah terjadi.