DynamoDB'de Singleton Item'lar
Bir singleton item, tüm uygulaman için durum tutan, sabit ve gömülü bir key'e sahip tek bir satırdır — kullanıcı başına ya da sipariş başına bir kayıt değil, bir kayıt, hepsi bu. Feature flag'ler, bir config blob'u, global bir acil durdurma anahtarı: ilişkisel bir uygulamanın tek satırlık bir ayarlar tablosunda tutacağı türden şeyler.
SQL'den geliyorsan, id = 1 ve bir SELECT * FROM config ile bir config
tablosuna uzanırdın. DynamoDB'de aynı şeyi gömülü bir partition key ile yaparsın —
ve o key'i her zaman bildiğin için, onu bir Query ya da Scan ile değil, bir
GetItem ile okursun.
DynamoDB'de singleton item nedir?
Bir singleton item, kullanıcı ya da sipariş başına bir kayıt yerine tüm uygulamanın global durumunu tutan — feature flag'ler, bir config blob'u, sistem genelinde bir sürüm — sabit ve gömülü bir key altında depolanan tek bir DynamoDB satırıdır. Key'i her zaman bildiğin için, onu GetItem ile okur ve ile birlikte condition expression'larla güncellersin.
- Bir singleton, sabit key'li tek bir item'dır. Bir kullanıcı ya da sipariş
id'sini şablona koymak yerine
PK/SK'yı (örn.CONFIG#GLOBAL) kodunda gömülü yazarsın. - Onu
GetItemile oku, aslaScanile. Tam key'i her zaman bilirsin, dolayısıyla bir nokta okuma tek bir tutarlı, öngörülebilir RCU'dur — filtre yok, tablo gezme yok. - Tanımı gereği bir hot key'dir. Her istek aynı partition'a dokunabilir, dolayısıyla onu cache'le ve item'ı küçük tut; onu bir yazma darboğazı yapma.
- Onu update + condition expression'larla güvenle değiştir, uygulamanda read-modify-write ile değil — kayıp-güncelleme yarışının yaşadığı yer orasıdır.
Deseni tanı
Veri herhangi bir entity'ye kapsamlanmadığında global durumun vardır. Birkaç işaret:
- Herkes için aynı olan bir flag (
signup_enabled = false). - Uygulamanın başlangıçta okuduğu bir ayarlanabilir parametre blob'u (rate limit'ler, varsayılan kotalar).
- Satır başına değil, tüm sistem için bir sayaç ya da sürüm numarası.
Bir kullanıcıya, kiracıya ya da siparişe kapsamlanmış herhangi bir şey singleton değildir — o, o entity'nin id'siyle key'lenmiş sıradan bir item'dır. Singleton, başka gidecek yeri olmayan, artakalan global dilimdir.
Ona sabit bir key ver
Tüm desen tek bir karara dayanır: key bir literal'dir, bir şablon değil. Aşırı yüklü bir tek tablodaki global bir feature-flag item'ı için sabit bir prefix ve sabit bir değer seç:
| PK | SK | attributes |
|---|---|---|
| SETTINGS#APP | FLAGS#V1 | signup_enabled, maintenance_mode, ai_search_enabled |
PK = "SETTINGS#APP" ve SK = "FLAGS#V1" koda gömülüdür. Kullanıcı id'si yok,
kiracı id'si yok — uygulama her seferinde tam olarak bu item'ı ister. O
öngörülebilirlik asıl mesele: bilinen bir key bir GetItem'dır ve bir GetItem,
DynamoDB'nin sunduğu en ucuz, en tutarlı okumadır.
V1 son eki kasıtlıdır. Flag şeması ileride şekil değiştirirse, canlı olanı yerinde
değiştirmek yerine bir FLAGS#V2 item'ı yazar ve okuyucuları ona geçirirsin.
Singleton key'ini sürümlemek sana temiz bir taşıma dikişi kazandırır.
Onu GetItem ile oku
Key tamamen bilindiği için, bir singleton için asla Query yapmaz ve asla Scan
yapmazsın. Bir Scan tüm tabloyu okur ve istemci tarafında filtreler — klasik
Scan ayak kapanı — ve doğrudan adresleyebileceğin tek bir
satırı çekmek için saçma bir aşırılıktır.
SETTINGS#APP / FLAGS#V1'e karşı bir GetItem, flag'leri tek bir güçlü ya da
nihai tutarlı okumada döndürür. AWS, ≤ 4 KB'lık bir item'ın GetItem'ını nihai
tutarlı için 0,5 RCU ya da güçlü tutarlı için 1 RCU olarak faturalandırır
(AWS okuma/yazma kapasitesi dokümanları).
Singleton'ı küçük tut, o maliyet sonsuza kadar düz kalsın.
Okuma yolu basitçe şu: uygulama başlar ya da bir istek gelir, sabit key'i GetItem
edersin, sonucu cache'lersin. İşte akış.
Sabit key, global bir aramayı yerleşik bir varsayılan yola sahip tek bir nokta okumaya dönüştürür.
hayır dalına dikkat et: eksik bir singleton seni asla çökertmemeli. Bir
ilk-deploy boşluğu ya da kötü bir key açık değil de kapalı düşsün diye güvenli
değere varsayılan yap (feature kapalı, bakım açık).
Onu yarış olmadan güncelle
Tuzak, bir singleton'ı uygulamanda read-modify-write ile güncellemektir: flag'leri
GetItem edersin, birini bellekte değiştirirsin, sonra tüm şeyi geri PutItem
edersin. İki eşzamanlı yazar da eski item'ı okur ve ikinci Put birincinin
değişikliğini ezer. Kayıp güncelleme.
İki DynamoDB özelliği, uygulama tarafı kilitleme olmadan yarışı öldürür:
- Update expression'lar sunucu tarafında tek bir attribute'ı değiştirir,
gerisini el değmemiş bırakır. Tüm item'ı yeniden
Putetmeye gerek yok. - Condition expression'lar yazmanın yalnızca item hâlâ beklediğin gibi
görünüyorsa başarılı olmasını sağlar, dolayısıyla bayat bir yazma
ConditionalCheckFailedExceptionile reddedilir (AWS condition expression dokümanları).
Tek bir flag'i değiştirmek için, yalnızca o attribute'ı bir SET ile hedefle ve
eşzamanlı yazarların birbirini ezememesi için bir sürüm artışıyla koru:
# UpdateItem
Key PK=SETTINGS#APP SK=FLAGS#V1
UpdateExpression SET signup_enabled = :on, schema_version = :next
ConditionExpression schema_version = :current
İki yazar yarışırsa, ikincinin schema_version = :current kontrolü başarısız olur
ve taze değere karşı yeniden dener. Name'leri, value'ları ve tam bu expression
şeklini koda bağlamadan önce
DynamoDB Expression Builder içinde
iskeletleyebilirsin. Operatörlere daha derin bir bakış için
update-expression idiom'ları kılavuzuna bak.
Hot key'e dikkat et
Bir singleton, yapısı gereği bir hot key'dir — uygulamanın her parçası aynı partition'ı okuyabilir. Cache'lersen okumalar için bu sorun değil, ama desenin tek gerçek riski budur.
- Agresif cache'le. Flag'leri her istekte değil, işlem başına (ya da N saniyede bir) bir kez oku. Singleton'ın değeri ezberlenecek en ucuz şeydir.
- Onu bir yazma sıcak noktası yapma. Bir yöneticinin günde birkaç kez değiştirdiği bir flag hiçbir şeydir. Her istekte arttırdığın bir singleton bir partition-throughput darboğazıdır — o bir sayaç problemidir, bir singleton değil.
- Onu küçük tut. Okuma maliyeti, item boyutuyla 4 KB'lık bloklar halinde ölçeklenir. Şişmiş bir config blob'u her başlangıcı gerektiğinden daha pahalı yapar.
Gerçekten yüksek-yazmalı global bir sayaca ihtiyacın varsa, singleton yanlış şekildir — onu N item'a parçala (shard) ve okumada topla. Bu farklı bir desendir.
Singleton vs entity başına item
Çizgi basitçe _verinin neye kapsamlandığı_dır.
| Singleton item | Entity başına item | |
|---|---|---|
| Key | Gömülü sabit (SETTINGS#APP) | Bir id ile şablonlanmış (USER#42) |
| Kaç tane | Tam olarak bir | Kullanıcı / sipariş / kiracı başına bir |
| Tipik okuma | Bilinen key üzerinde GetItem | Entity'ye göre GetItem ya da Query |
| Kapsam | Tüm uygulama | Tek bir entity |
| Şunun için kullan | Global flag'ler, config, sistem sürümü | Profiller, siparişler, id başına her şey |
Aynı türden iki singleton istediğini fark edersen, bir singleton'ın yok — bir entity başına item'ın var ve key'lemeyi unuttuğun şey o entity (örneğin kiracı başına config).
Tuzaklar ve sonraki adımlar
- Onun için
Scanyapma. Key'i biliyorsun; onu doğrudan adresle. - Onu read-modify-write etme. Update + condition expression'lar kullan.
- Onun sessizce kaybolmasına izin verme. Bir cache miss'inde güvenli değere varsayılan yap.
- Onu yüksek-frekanslı yazmalarla aşırı yükleme. Bu bir parçalanmış-sayaç işidir.
Singleton bir single-table design içinde rahatça yaşar — entity satırlarının yanında sabit bir key'e sahip yalnızca bir item koleksiyonu daha.
Tablona göz atmak, singleton satırı sabit key'iyle bulmak ve yazma yolunu kurarken flag'leri elle düzenlemek için DynoTable'ı dene.