Başlangıç5 dakikalık okuma

DynamoDB Projection Expression'ları

Bir projection expression, DynamoDB'nin SELECT col1, col2'sidir: GetItem'a, Query'ye veya Scan'e tüm öğe yerine yalnızca o attribute'ları döndürmesini söyleyen, virgülle ayrılmış bir attribute adları listesi.

DynamoDB projection expression'ları okuma maliyetini azaltır mı?

Hayır. ProjectionExpression, yanıt payload'ını kırpar, ancak faturalandırılan okuma kapasitesini değil. DynamoDB tam öğeyi depolamadan okur, disk üzerindeki boyutuna göre hesaplar, ardından çıkış yolunda adlandırmadığın attribute'ları düşürür. Okuma maliyetini gerçekten düşürmek için bunun yerine bir kapsayan kullan.

  • Payload'ı kırpar, okuma maliyetini değil. DynamoDB tam öğeyi depolamadan okur (ve faturalandırır), sonra çıkış yolunda adlandırmadığın attribute'ları düşürür. ProjectionExpression bir ağ optimizasyonudur, bir kapasite optimizasyonu değil.
  • Genel bir alt kümeyi getirmenin yoludur. Bir çağıranın görmesine izin verilen birkaç attribute'u adlandır; geri kalanı tabloyu hiç terk etmez.
  • Rezerve olabilecek herhangi bir şey için #name placeholder'larını kullan. İfadedeki düz attribute adları DynamoDB'nin ~570 rezerve kelimesiyle çakışır ve isteği başarısız kılar.
  • Gerçek okuma tasarrufu için, bunun yerine bir kapsayan (covering) index kullan. Yalnızca ihtiyacın olan sütunları yansıtan bir GSI, kendi (daha küçük) boyutunda okunur.

Gerçekte neyi tasarruf ettirir

SQL'den gelince, SELECT a, b'nin SELECT *'tan daha az tarayacağını varsayardın. DynamoDB'de o sezgi yanlıştır. Bir okumanın kapasite birimi, projection uygulanmadan önce, disk üzerindeki öğenin boyutundan, bir sonraki 4 KB'ye yuvarlanarak hesaplanır. AWS açıktır: bir ProjectionExpression, bir isteğin tükettiği okuma kapasitesini değiştirmez.1

Yani bir projection sana iki şey tasarruf ettirir, ikisi de gerçek ama ikisi de okumanın aşağı akışında:

  • Tel üzerindeki byte'lar. İki küçük attribute olarak döndürülen bir 6 KB öğe çok küçük bir yanıttır. Yüzlerce öğe döndüren bir Query'de, bu hızla birikir.
  • İstemci tarafı iş. Deserialize edilecek daha az, bellekte tutulacak daha az, bir log'a veya bir API yanıtına yanlışlıkla sızdırılacak daha az.

Tasarruf ettirmediği şey RCU'dur. İşte ayak tuzağı: insanlar faturalarını kısmak için bir projection'a uzanır, hiçbir değişiklik görmez ve DynamoDB'nin bozuk olduğu sonucuna varır. Bozuk değil — yanlış kaldıracı ölçtün.

Bir genel kullanıcı profili yansıt

Diyelim ki bir kullanıcı dizini işletiyorsun. Her profil tek bir öğedir, bir kişiyi kullanıcı adıyla getirebilecek şekilde anahtarlanmıştır:

PK = "PROFILE#ada"      (partition key)
SK = "PROFILE#ada"      (sort key — tek öğeli collection)

Öğe şişmandır. Hesabın genel yüzünü artık bir yığın özel ve operasyonel attribute'u taşır:

{
  "PK": "PROFILE#ada",
  "SK": "PROFILE#ada",
  "displayName": "Ada L.",
  "avatarUrl": "https://cdn.example.com/u/ada.png",
  "bio": "Builds things.",
  "emailAddress": "ada@example.com",
  "passwordResetToken": "…",
  "billingCustomerId": "cus_…",
  "lastLoginIp": "…",
  "internalRiskScore": 0.02
}

Bir genel profil kartının üç alana ihtiyacı var. Tüm öğeyi getirmek, emailAddress, lastLoginIp ve internalRiskScore'un bunları asla görmemesi gereken bir bağlama gitmesi demektir. Yalnızca genel alt kümeyi adlandır:

GetItem  PK = "PROFILE#ada"  SK = "PROFILE#ada"
ProjectionExpression: displayName, avatarUrl, bio

Yanıt üç attribute taşır. Özel olanlar tabloda kalır — gelişlerinden sonra uygulaman tarafından filtrelenmez, ama hiçbir zaman yanıta serialize edilmez. İşte güvenlik kazancı budur ve bir sır bir sınırı zaten geçtikten sonra geri alması zor olan kazanç budur.

Bu tam isteği — adlar, placeholder'lar ve SDK çağrısı — DynamoDB Expression Builder'da birleştirip kopyalayabilirsin; senin için ProjectionExpression'ı ve ExpressionAttributeNames eşlemesini yayar.

Rezerve kelimeleri # placeholder'larıyla kaçır

İşte temiz bir projection'ın patladığı yer. DynamoDB uzun bir kelime listesini rezerve eder — name, status, comment, size, timestamp ve yüzlercesi daha.2 Yansıttığın bir attribute bunlardan biriyse, ifadedeki ham ad reddedilir.

Diyelim ki profilin ayrıca bir status attribute'u var ("active", "suspended"). Bu başarısız olur:

ProjectionExpression   displayName, status

status rezervedir. Düzeltme bir expression attribute name'dir — gerçek ada eşlenmiş #-önekli bir placeholder:

ProjectionExpression       displayName, #s
ExpressionAttributeNames   { "#s": "status" }

Aynı mekanizma iç içe geçmiş attribute'lara uzanır. Bir map'ten tek bir alanı veya bir listenin bir elemanını çekmek için, doküman-yolu söz dizimini kullan — ve her segmenti placeholder ile koru, çünkü herhangi biri rezerve olabilir:

ProjectionExpression       #addr.#city, tags[0]
ExpressionAttributeNames   { "#addr": "address", "#city": "city" }

Pratik bir kural: her şeyi placeholder ile koru. ~570 rezerve kelimenin hangisinin üzerinde durduğunu asla hatırlamak zorunda kalmazsın ve ifade her iki şekilde de aynı okunur.

Bir kapsayan index bir projection'ı ne zaman yener

Gerçekten okuma maliyetini kısman gerekiyorsa — yalnızca payload'ı değil — kaldıraç, yalnızca okuduğun attribute'ları yansıtan bir Global Secondary Index'tir. Bir GSI, verinin ayrı bir kopyasıdır; yansıtması için KEYS_ONLY, INCLUDE veya ALL seçersin.3 Bir KEYS_ONLY veya dar INCLUDE index'i öğe başına fiziksel olarak daha küçüktür, bu yüzden ona karşı bir Query o daha küçük boyutta ölçülür.

İşte bir kapsayan index budur: sorgu tamamen index'ten yanıtlanır, temel tabloya geri dönüş yok. Sıcak bir okuma deseni yalnızca büyük öğelerden birkaç attribute'a ihtiyaç duyduğunda kullan.

ProjectionExpressionKapsayan GSI
Payload'ı kısarEvetEvet
Okuma maliyetini kısarHayırEvet — index'in boyutunda okunur
Ekstra depolamaYokYansıtılan alanların ikinci bir kopyası
Ekstra yazma maliyetiYokYazmalar index'e yayılır
En iyisiÖzel alanları gizlemek; küçük kazançlarBüyük öğelerden birkaç alanın sıcak okumaları

Ödünleşim dürüsttür: index, okuma kapasitesinden tasarruf etmek için sana depolama ve yazma kapasitesine mal olur. Ağır bir öğeden ince bir dilimin sık bir okuması için buna değer; bir kez-seferlik bir GetItem'ı kısaltmak için değmez. Index türünü seçmek için bkz. GSI ve LSI ve bir tanesini sıcak yola koymadan önce bir GSI okuması ne zaman eski olabilir.

Tuzaklar ve sonraki adımlar

  • Daha küçük bir fatura bekleme. Yalnızca bir projection asla RCU'yu değiştirmez. Sayı hareket etmediyse, bu belgelenmiş davranıştır, bir hata değil.
  • Rezerve kelimeleri placeholder ile koru. İfadede çıplak bir name veya status isteği başarısız kılar — #-eşlemesini yap.
  • Anahtar attribute'ları yansıtmak bedavadır ve genellikle yararlıdır — DynamoDB onları ucuza döndürür ve sayfalamana veya yeniden getirmene izin verirler.
  • Yalnızca sıcak bir desen büyük öğelerden birkaç alan okuduğunda bir kapsayan index'e uzan; önce yazma/depolama maliyetini tart.

ProjectionExpression'ı ve attribute-adı eşlemesini Expression Builder'da oluştur ve bu projection'ları kendi tablolarına karşı çalıştırmak ve yanıtın küçüldüğünü izlemek için DynoTable'ı dene.


  1. AWS DynamoDB Developer Guide, Working with Read and Write Operations — okuma kapasitesi, herhangi bir ProjectionExpression uygulanmadan önceki öğe boyutuna dayanır. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/
  2. AWS DynamoDB Developer Guide, Reserved Words in DynamoDB. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
  3. AWS DynamoDB Developer Guide, Attribute Projections (KEYS_ONLY / INCLUDE / ALL). https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html

Güncellendi