Saya telah beroperasi dengan asumsi bahwa satu pernyataan di SQL Server konsisten
Anggapan itu salah. Dua transaksi berikut memiliki semantik penguncian yang identik:
STATEMENT
BEGIN TRAN; STATEMENT; COMMIT
Tidak ada perbedaan sama sekali. Pernyataan tunggal dan komit otomatis tidak mengubah apa pun.
Jadi menggabungkan semua logika menjadi satu pernyataan tidak membantu (jika ya, itu tidak sengaja karena rencananya berubah).
Mari kita perbaiki masalah yang ada. SERIALIZABLE
akan memperbaiki ketidakkonsistenan yang Anda lihat karena ini menjamin bahwa transaksi Anda berperilaku seolah-olah mereka dieksekusi secara single-threaded. Secara setara, mereka berperilaku seolah-olah mereka dieksekusi secara instan.
Anda akan mendapatkan jalan buntu. Jika Anda setuju dengan pengulangan percobaan, Anda sudah selesai pada tahap ini.
Jika Anda ingin menginvestasikan lebih banyak waktu, terapkan petunjuk penguncian untuk memaksa akses eksklusif ke data yang relevan:
UPDATE Gifts -- U-locked anyway
SET GivenAway = 1
WHERE GiftID = (
SELECT TOP 1 GiftID
FROM Gifts WITH (UPDLOCK, HOLDLOCK) --this normally just S-locks.
WHERE g2.GivenAway = 0
AND (SELECT COUNT(*) FROM Gifts g2 WITH (UPDLOCK, HOLDLOCK) WHERE g2.GivenAway = 1) < 5
ORDER BY g2.GiftValue DESC
)
Anda sekarang akan melihat pengurangan konkurensi. Itu mungkin baik-baik saja tergantung pada beban Anda.
Sifat masalah Anda membuat pencapaian konkurensi menjadi sulit. Jika Anda memerlukan solusi untuk itu, kami perlu menerapkan teknik yang lebih invasif.
Anda dapat menyederhanakan UPDATE sedikit:
WITH g AS (
SELECT TOP 1 Gifts.*
FROM Gifts
WHERE g2.GivenAway = 0
AND (SELECT COUNT(*) FROM Gifts g2 WITH (UPDLOCK, HOLDLOCK) WHERE g2.GivenAway = 1) < 5
ORDER BY g2.GiftValue DESC
)
UPDATE g -- U-locked anyway
SET GivenAway = 1
Ini menghilangkan satu gabungan yang tidak perlu.