UPDATE / INSERT kombinasyonu için Postgres'te kilitleme


11

İki masam var. Birincisi bir günlük tablosu; diğeri, esasen, yalnızca bir kez kullanılabilen kupon kodları içerir.

Kullanıcının, günlük tablosuna bir satır ekleyecek ve kuponu kullanıldığı gibi işaretleyecek ( usedsütunu güncelleyerek true) bir kuponu kullanabilmesi gerekir .

Doğal olarak, burada bariz bir yarış durumu / güvenlik sorunu var.

Geçmişte mySQL dünyasında benzer şeyler yaptım. Bu dünyada, her iki tabloyu da küresel olarak kilitlerdim, bunun her seferinde sadece bir kez olabileceği bilgisini güvence altına alırdım ve sonra bir kez yapıldıktan sonra masaların kilidini açardım.

Postgres'de bunu yapmanın daha iyi bir yolu var mı? Özellikle, kilit küresel, ancak olmak zorunda değilim - Gerçekten sadece kimsenin bu belirli kodu girmeye çalıştığından emin olmak gerekir, bu yüzden belki de bazı satır düzeyinde kilitleme çalışır?

Yanıtlar:


15

Daha önce MySQL'de böyle bir eşzamanlılık problemini duymuştum. Postgres'de böyle değil.

Varsayılan READ COMMITTEDişlem yalıtım düzeyindeki yerleşik satır düzeyi kilitler yeterlidir.

Veri değiştiren CTE (MySQL'in sahip olmadığı bir şey) ile tek bir ifade öneriyorum çünkü değerleri bir tablodan diğerine doğrudan geçirmek uygun (buna ihtiyacınız varsa). couponTablodan herhangi bir şeye ihtiyacınız yoksa, ayrı UPDATEve INSERTifadeleri olan bir işlemi de kullanabilirsiniz .

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;

Birden fazla işlemin aynı kuponu kullanmaya çalışması nadir görülen bir şey olmalıdır . Benzersiz bir sayıları var, değil mi? Aynı anda çalışmakta olan birden fazla işlemin daha nadir olması gerekir. (Belki bir uygulama hatası veya sistemi oynamaya çalışan biri?)

Ne olursa olsun , UPDATEsadece tam olarak bir işlem için başarılı olur . Bir , güncellenmeden önce her hedef satırda UPDATEbir satır düzeyinde kilit alır. Eşzamanlı bir işlem UPDATEaynı satıra çalışırsa, satırdaki kilidi görür ve engelleme işlemi bitinceye ( ROLLBACKveya COMMIT) kadar bekler , ardından kilit kuyruğundaki ilk işlem olur :

  • Taahhüt edilirse, durumu tekrar kontrol edin. Hala devam ediyorsa NOT used, satırı kilitleyin ve devam edin. Else UPDATEartık hiçbir eleme satır bulur ve yapar bir şey bu yüzden, hiçbir satır döndüren, INSERTayrıca hiçbir şey yapmaz.

  • Geri alınırsa, satırı kilitleyin ve devam edin.

Orada bir yarış durumu için herhangi bir potansiyel .

Orada bir imkanı yok çıkmaz , aynı tür bir işlemi daha yazıyor koyabilir veya sadece birden fazla satır kilitlemek sürece.

INSERTBakım gerektirmez. Bazı durumlarda yanlışlıkla coupon_idzaten logtablodaysa (ve EŞSİZ veya PK kısıtlamanız varsa log.coupon_id), tüm işlem benzersiz bir ihlalden sonra geri alınır. DB'nizde geçersiz bir durumu gösterir. Yukarıdaki ifade logtabloya yazmanın tek yolu ise , bu asla gerçekleşmemelidir.


Gerçekten de, birden fazla işlemin aynı kodu kullanmaya çalıştığı nadir bir şey olmalı, ancak şüphelileriniz, bunun sadece birisinin sistemi oynamaya çalıştığı zaman olacağı yönündedir. Bunun için çok teşekkürler - CTE'ler Postgres'e taşınmak için benim için büyük bir çekiciydi, ancak örtük kilitlemenin bunun için yeterince iyi olacağını fark etmedim.
Rob Miller
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.