İki kullanıcı son koltuğu aynı anda alıyor. İki istek de “stok 1” okuyor, ikisi de satış yapıyor: çift rezervasyon. Sorun SQL'in yanlış yazılması değil, okuma ile yazma arasındaki boşluk. Bu derste bu boşluğu kapatmanın dört yolunu görürsün.
1. Tek ifadede koşullu güncelleme
Çoğu durumda en basit ve en güvenli yol budur. Okumayı ve yazmayı tek ifadede birleştir, sonra etkilenen satır sayısına bak: 0 ise stok bitmiştir.
UPDATE products SET stock = stock - 1 WHERE product_id = 7 AND stock >= 1;
2. Kötümser kilit: FOR UPDATE
Okuduğun değere göre uygulamada karar vereceksen satırı okurken kilitle. İkinci oturum, birincisi işlemi bitirene kadar bekler.
BEGIN; SELECT stock FROM products WHERE product_id = 7 FOR UPDATE; -- uygulama kararını verir UPDATE products SET stock = stock - 1 WHERE product_id = 7; COMMIT;
| SQL türü | Satırı kilitleyerek okuma | Kilitli satırı atlama |
|---|---|---|
| PostgreSQL, MySQL 8+, Oracle | FOR UPDATE | FOR UPDATE SKIP LOCKED |
| SQL Server | WITH (UPDLOCK, ROWLOCK) | WITH (UPDLOCK, READPAST) |
| SQLite, BigQuery | Yok | Yok |
3. İş kuyruğu: SKIP LOCKED
Birden çok işçi aynı kuyruktan iş çekiyorsa FOR UPDATE onları sıraya dizer. SKIP LOCKED kilitli satırı atlar, her işçi boşta olan bir sonraki işi alır.
BEGIN; SELECT job_id FROM jobs WHERE status = 'bekliyor' ORDER BY job_id LIMIT 1 FOR UPDATE SKIP LOCKED; UPDATE jobs SET status = 'calisiyor' WHERE job_id = :secilen; COMMIT;
4. İyimser kilit ve yeniden deneme
Çakışma seyrekse kilit tutmak yerine sürüm sütunu kullan: UPDATE ... SET surum = surum + 1 WHERE id = :id AND surum = :okunan. Etkilenen satır 0 ise başkası önce davranmıştır; yeniden oku ve dene.
Deadlock ve serileştirme hataları normaldir, kod bunlara hazır olmalı: PostgreSQL 40P01 ve 40001, SQL Server 1205 döndürür. İşlemi baştan, az sayıda ve kısa beklemeyle yeniden dene. Tek ifadenin bir bölümünü değil, bütün işlemi tekrarla.