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.
ProjectionExpressionbir 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
#nameplaceholder'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.
ProjectionExpression | Kapsayan GSI | |
|---|---|---|
| Payload'ı kısar | Evet | Evet |
| Okuma maliyetini kısar | Hayır | Evet — index'in boyutunda okunur |
| Ekstra depolama | Yok | Yansıtılan alanların ikinci bir kopyası |
| Ekstra yazma maliyeti | Yok | Yazmalar index'e yayılır |
| En iyisi | Özel alanları gizlemek; küçük kazançlar | Bü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
nameveyastatusisteğ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.
- AWS DynamoDB Developer Guide, Working with Read and Write Operations — okuma kapasitesi, herhangi bir
ProjectionExpressionuygulanmadan önceki öğe boyutuna dayanır. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ ↩ - AWS DynamoDB Developer Guide, Reserved Words in DynamoDB. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html ↩
- AWS DynamoDB Developer Guide, Attribute Projections (
KEYS_ONLY/INCLUDE/ALL). https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html ↩