DynamoDB Condition Expression: Eksiksiz Rehber (Örneklerle)
Bir koşul ifadesi, DynamoDB'nin yazmanı işlemeden önce mevcut öğe üzerinde
değerlendirdiği bir yüklemdir. Yüklem yanlışsa yazma reddedilir ve hiçbir şey
değişmez. Bu, DynamoDB'nin bir yazma üzerinde bir WHERE cümlesine en yakın
sahip olduğu şeydir — ve bir değişmezi uygulamanın tek güvenli yoludur.
DynamoDB koşul ifadeleri nasıl çalışır?
Bir koşul ifadesi, DynamoDB'nin bir yazmayı işlemeden önce mevcut öğeye karşı
sunucu tarafında değerlendirdiği bir yüklemdir. Doğruysa yazma devam eder;
yanlışsa yazma ConditionalCheckFailedException ile reddedilir ve hiçbir şey
değişmez. Kontrolü ve mutasyonu tek bir atomik işlemde birleştirir, böylece eş
zamanlı çağıranlar eski bir okumada yarışamaz.
- Bu bir koruma, bir filtre değil.
ConditionExpression, mevcut öğe üzerinde sunucu tarafında çalışır; yanlış bir sonuç yazmayıConditionalCheckFailedExceptionile başarısız kılar. - Oku-sonra-yaz'ın yerini alır.
SELECTsonraUPDATEgidiş-dönüşü yok — kontrol ve mutasyon tek bir atomik işlemdir, böylece iki çağıran yarışamaz. - Reddetmesi bedava, çalışması değil. Başarısız bir koşullu yazma yine de yazma kapasitesi tüketir. Reddedilen bir yazma, kontrol edildiği mevcut öğenin boyutu için WCU faturalandırır (minimum 1) — başarısız bir yoksa-oluştur 1 WCU tutar.
SQL'den geliyorsan, satırı okur, uygulama kodunda kontrol eder, sonra güncellersin. DynamoDB'de okuma ile yazma arasındaki o boşluk, eş zamanlı bir çağıranı bekleyen bir veri bozulması hatasıdır. Koşul ifadesi bu boşluğu kapatır.
Nerede geçerli olurlar
Bir ConditionExpression'ı PutItem, UpdateItem, DeleteItem'a ve
TransactWriteItems içindeki her eyleme iliştirirsin. Query veya Scan'in
parçası değildir — onlar FilterExpression kullanır ki bu, okuma yolunda
farklı bir şeydir.
Bu ayrım insanların ayağını kaydırır, o yüzden hassas ol:
ConditionExpression | FilterExpression | |
|---|---|---|
| Yol | Yazmalar (Put/Update/Delete) | Okumalar (Query/Scan) |
| Başarısızlıkta etki | Tüm yazmayı reddeder | Öğeyi sonuçlardan düşürür |
| Gördüğü | Mevcut öğe, yazma öncesi | Her aday öğe, okuma sonrası |
| Maliyet | Başarısız yazma yine faturalanır | Filtrelenen öğeler okuma için yine faturalanır |
İkisi de sunucu tarafında çalışır. Fark, "yanlış"ın ne yaptığıdır: bir koşul bir mutasyonu iptal eder; bir filtre ise okumak için zaten ödediğin bir satırı sadece gizler. (AWS: Koşul ifadeleri)
Gerçekten kullanacağın fonksiyonlar
Koşul dili küçüktür. İş atları:
attribute_exists(path)/attribute_not_exists(path)— bu öğede var mı? "Yalnızca yoksa oluştur" / "yalnızca varsa güncelle" için klasik deyim.- Karşılaştırıcılar —
=,<>,<,<=,>,>=— bir değere veya başka bir özniteliğe karşı. attribute_type,begins_with,contains,size— tür ve string/küme kontrolleri.BETWEEN … AND …,IN (…)— aralık ve üyelik.AND,OR,NOT, parantezler — yukarıdakileri birleştirmek için.
üzerinde attribute_not_exists, PutItem'ı mevcut
bir öğenin üzerine yazmayacak bir insert gibi davrandırmanın kanonik yoludur —
DynamoDB'nin ayrı bir "insert" işlemi yoktur, dolayısıyla koşul insert
anlambilimidir.
(AWS: Karşılaştırma operatörü ve fonksiyon referansı)
İşlenmiş bir örnek: bir defterin eksiye düşmesini önleme
Bir banka defteri düşün. Her hesap tek bir öğedir:
PK = "ACCT#a7f3"
SK = "BALANCE"
clearedCents = 50000
holdCents = 0bir çekim, kullanılabilir bakiyeyi asla sıfırın altına itmemeli ve mevcut olmayan bir hesaptan asla çekim yapmamalısın. İki kural, ikisi de yazmanın kendisinde uygulanabilir.
Yanlış yol (tuzak)
GetItem ACCT#a7f3 / BALANCE → clearedCents = 50000
if (50000 >= 30000) ... ← app-side check
UpdateItem SET clearedCents = 20000
GetItem ile UpdateItem arasında, ikinci bir çekim aynı 50000'i okuyabilir,
kendi kontrolünü geçebilir ve o da yazabilir. İkisi de başarılı olur; hesap eksiye
düşer. Bu bir oku-değiştir-yaz yarışıdır ve hiçbir miktar uygulama tarafı
doğrulaması onu düzeltmez — kontrol ve yazma ayrı işlemlerdir.
Doğru yol
Kontrolü yazmanın içine katla. 30000 sent çek, hesabın var olmasına ve yeterli tutmasına koşullu olarak:
UpdateItem ACCT#a7f3 / BALANCE
SET clearedCents = clearedCents - :amt
ConditionExpression:
attribute_exists(PK) AND clearedCents >= :amt:amt = 30000 ile. Bakiye çok düşükse veya öğe hiç oluşturulmadıysa, DynamoDB
yazmayı
ConditionalCheckFailedException
ile reddeder ve bakiyeye dokunulmaz. Eş
zamanlı çekim ya orijinal bakiyeyi görür ve ona karşı kontrol edilir ya da
güncellenmişini görür — üzerine iş yaptığı eski bir okumayı asla görmez.
Tam ifadeyi — adlar, değerler ve her şey dahil —
ExpressionAttributeValues haritasını elle birleştirmek yerine
DynamoDB expression builder ile oluşturup
kopyalayabilirsin.
Hemen burada dene — bu builder, korumalı bir PutItem
(attribute_not_exists) için önceden ayarlıdır, böylece üretilen
ConditionExpression'ı okuyabilirsin:
DynoTable'da korumayı inceleme
Bir koşullu yazma başarısız olduğunda, öğenin gerçek durumunu tahmin etmek değil,
görmek istersin. Hesap öğesini çek ve clearedCents'i doğrudan oku.

Reddi oku, körlemesine yeniden deneme
ConditionalCheckFailedException geçici bir hata değildir — aynı yazmayı yeniden
denemek hiçbir şeyi değiştirmez. Bir iş kuralının tetiklendiği anlamına gelir:
yetersiz bakiye, mükerrer oluşturma, eski sürüm. Onu bir altyapı arızası olarak
değil, bir alan sonucu olarak yüzeye çıkar.
İki şey başarısızlıkları hata ayıklanabilir kılar:
ReturnValuesOnConditionCheckFailure: ALL_OLD— DynamoDB, başarısızlıkla birlikte mevcut öğeyi döndürür, böylece ikinci bir okuma olmadan "bakiye 20000 idi, sen 30000 istedin" gösterebilirsin. (AWS: Öğelerle çalışma)- İki başarısızlık nedenini ayırt etme.
attribute_exists(PK) AND clearedCents >= :amt, "hesap yok" ile "bakiye yok"u tek bir istisnaya çöker. Çağıranların bunları ayırması gerekiyorsa, iki yazmaya böl veya döndürülen öğeyi incele.
İyimser kilitleme aynı numaradır
Sürüm-numarası deseni, farklı bir şapka takmış bir koşul ifadesinden başka bir şey
değildir. Bir version özniteliği sakla; her yazma, okuduğun sürümü ileri sürer ve
onu artırır:
UpdateItem ACCT#a7f3 / BALANCE
SET clearedCents = :new, version = :next
ConditionExpression: version = :seenBaşka bir yazan önce hamle yaptıysa, version = :seen yanlıştır, yazma reddedilir
ve yeniden okuyup yeniden denersin. DynamoDB, kilit olmadan eş zamanlılık kontrolünü
böyle yapar — gördüğünü ileri sür, değiştiyse başarısız ol. (AWS: Optimistic Locking with
Version Number)
DynoTable'ın hazırlama alanı bu deseni senin için çalıştırır —
eş zamanlı bir düzenleme, kaybolan bir yazma olarak değil, çözülecek bir çakışma
olarak yüzeye çıkar.
Tuzaklar ve sonraki adımlar
- Ayrılmış sözcüklerle çakışan adlar.
status,size,nameve ~570 başkası ayrılmıştır. BunlarıExpressionAttributeNamesile takma adlandır (#s = status), yoksa istek bir ValidationException ('Attribute name is a reserved keyword') ile reddedilir. Ayrılmış sözcükler denetleyicisi, öznitelik adlarını alır ve takma ad haritasını yapıştırmaya hazır geri verir. - Bir koşul başka bir öğeye başvuramaz. Yalnızca yazılan öğeyi görür.
Öğeler-arası değişmezler, eylem başına bir
ConditionExpressioniçerenTransactWriteItems'a ya da bir sentinel öğeye karşı birConditionCheck'e ihtiyaç duyar. - Başarısız yazmalar yine WCU tutar. Zamanın %90'ında reddeden bir koruma yine o reddedilenler için faturalandırır. Ucuz sigorta, ama bedava değil.
Bu korumaların üzerinde çalıştığı anahtarları modellemek için bkz. tek tablo tasarımı ve Query vs Scan. Gerçek veriye karşı koşullu yazmalar yapmaya hazır olduğunda, DynoTable'ı indir ve bunları kendi tablolarına karşı çalıştır.


