DynamoDB GROUP BY: Nasıl Toplama Yapılır
DynamoDB'de GROUP BY yoktur. COUNT, SUM veya AVG de yoktur — ne yerel
API'de ne de PartiQL'de. DynamoDB bir anahtar-değer / belge deposudur, bir analitik
motoru değil, dolayısıyla toplama, sorgu planlayıcısının senin için yaptığı bir şey
değil, senin inşa ettiğin bir şeydir.
DynamoDB'de GROUP BY yapabilir misin?
Hayır. DynamoDB'nin GROUP BY, HAVING veya COUNT, SUM ve AVG gibi toplama
fonksiyonları yoktur — ne yerel API'de ne de SELECT'i yalnızca WHERE ve
ORDER BY kabul eden PartiQL'de. Toplamayı, veri değiştikçe toplamları önceden
hesaplayarak (atomik sayaçlar veya + Lambda rollup'ları) ya da
okuduktan sonra uygulama tarafında gruplayarak yaparsın.
- DynamoDB'nin PartiQL
SELECTgrameriSELECT … FROM … [WHERE …] [ORDER BY …]'dir — ve bütün liste bu.GROUP BYyok,HAVINGyok, toplama fonksiyonu yok,JOINyok (AWS PartiQLSELECTreferansı). - DynamoDB "öğeler arasında
SUMveyaCOUNTgibi toplama işlemlerini yerel olarak desteklemediği" için, AWS'nin kendi rehberliği, toplamları veri değiştikçe önceden hesaplamak ve sonuçları sıradan öğeler olarak saklamaktır (AWS: materialize edilmiş toplama). - Alternatif — her öğeyi oku sonra uygulamanda topla — işe yarar, ama her sorguda tüm tabloyu okumaya ödersin.
- Tek seferlik keşif için, DynoTable'ın SQL Workbench'i
GROUP BY/COUNT/SUM/AVG'yi doğrudan canlı bir tabloya karşı çalıştırır — DynamoDB'nin PartiQL endpoint'inin reddettiği SQL.
DynamoDB'de toplama neden zor
DynamoDB'nin tarama zamanı bir toplama motoru yoktur. Query ve Scan öğeleri
döndürür; onları katlamazlar. Bir Scan, tüm tabloyu 1 MB'lık dilimlerle okur ve
tükettiği kapasite, tuttuğun satırlara değil, okuduğu öğelere dayanır — bir
FilterExpression, taramadan sonra ama sonuçlar dönmeden önce uygulanır,
dolayısıyla faturayı düşürmeden sonuç kümesini daraltır (AWS Scan API
referansı:
bir filtre "herhangi bir ek okuma kapasitesi birimi tüketmez"; kapasite döndürülene
değil, taranan öğe boyutuna dayanır). En başta bir toplam veya sayım asacak bir
GROUP-BY kancası da yoktur.
PartiQL bunu değiştirmez. PartiQL, aynı motor üzerinde SQL-uyumlu bir lehçedir,
dolayısıyla aynı sınırları miras alır — yeni bir yürütme modeli değil, bir söz dizimi
yüzeyidir. Belgelenmiş SELECT grameri
basitçe bir GROUP BY token'ına sahip değildir.
PartiQL ile gerçek SQL arasındaki tam boşluk için bkz.
PartiQL vs SQL.
Yani soru "bir GROUP BY'ı nasıl yazarım" değil — "toplamam nerede yaşıyor ve ne
zaman hesaplanıyor" sorusudur. Üç yanıt var.
Desen 1: yazmada topla (atomik sayaçlar)
Grupları önceden biliyorsan — durum başına sayım, müşteri başına toplam, ay başına indirme — bir sayaç öğesi tut ve her yazmada onu güncelle.
Artırım atomik ve eş zamanlılık açısından güvenli olsun diye bir ADD
kullan. ADD, sayılar ve kümeler üzerinde
çalışır ve oku-değiştir-yaz yarışından kaçınır, böylece aynı sayacı artıran iki
yazan birbirini asla ezmez
(AWS, atomik ADD'nin "oku-değiştir-yaz yarış koşullarından kaçındığını" belirtir):
UpdateItem
Key { pk: "STATS#orders", sk: "status#shipped" }
UpdateExpression "ADD orderCount :one"
ExpressionAttributeValues { ":one": 1 }
Bu, senin SELECT COUNT(*) … GROUP BY status'undur — ancak sayım zaten bir öğe
olarak orada oturuyor, tek haneli milisaniyelik bir GetItem'da okunabilir.
Takas: gruplama anahtarını yazma zamanında bilmen gerekir ve sayaç güncellemesini
yazma yoluna bağlarsın. Uygulama, yazmadan sonra ama sayaç güncellemesinden
önce çökerse, ikisi senkrondan çıkar — ki bu, tam olarak bir sonraki desenin
ayırdığı başarısızlık modudur.
Desen 2: DynamoDB Streams + Lambda rollup'ları
Toplama mantığını yazma yolunda istemediğinde — ya da yazma, kolayca saramayacağın
düz bir PutItem olduğunda — onu aşağı akışa taşı. Bu, AWS'nin kendi önerdiği
desendir, materialize edilmiş toplama
(AWS: Materialize edilmiş toplama sorguları için GSI kullanma):
- Uygulama ham öğeyi yazar (bir sipariş, bir indirme, bir olay). Toplama mantığı yok.
- , yazmayı bir akış kaydı olarak yakalar.
- Akışa bağlı bir Lambda, yeni öğeyi okur, grubu türetir (durum, ay,
kategori…) ve atomik bir
UpdateItemile eşleşen toplama öğesineADDekler — ki bu, birçok çağrı aynı sayaca dokunduğunda "oku-değiştir-yaz yarış koşullarından kaçınır". - Önceden hesaplanmış toplamayı sorgularsın — genellikle yalnızca rollup öğelerini
indeksleyen bir aracılığıyla, böylece "bu ay ilk
10",
Limit 10ile tek birQueryolur.
Sparse GSI numarası: yalnızca toplama öğeleri indekslenen özniteliği (örn.
Month) taşır, dolayısıyla ham olay satırları index'ten otomatik olarak dışlanır
— "tablodaki toplam öğelerin küçük bir kesri," ki bu index'i ucuz ve okumayı hızlı
tutar.
Bu, toplamayı yazma yolundan ayırır ve yazmaları basit tutar, nihai tutarlılık maliyetiyle — AWS, "bir indirmenin kaydedilmesi ile toplamanın güncellenmesi arasında birkaç saniyelik bir gecikme" olduğunu belirtir. Gösterge panelleri, lider tabloları ve trend sayaçları için bu sorun değildir.
yeniden denenen bir Lambda çağrısı ADD'yi
yeniden çalıştırır, dolayısıyla "bir yeniden deneme sayımı birden fazla artırır" ve
yaklaşık bir değer bırakır. Kesin sayımlar için idempotentlik ekle (örn. kaynak
öğenin id'sine anahtarlanmış bir koşul ifadesi); aksi hâlde küçük pay, analitik ve
lider tabloları için sorun değildir.
Desen 3: Scan/Query sonrası uygulama tarafı gruplama
Ya da öğeleri oku, onları kodunda grupla.
groups = {}
resp = table.scan() # or query() for one partition
while True:
for item in resp["Items"]:
key = item["status"]
groups[key] = groups.get(key, 0) + 1
if "LastEvaluatedKey" not in resp:
break
resp = table.scan(ExclusiveStartKey=resp["LastEvaluatedKey"])Bu doğrudur ve bazen doğru seçimdir — ama maliyet konusunda dürüst ol. Bir Scan,
tablodaki her öğeyi okur ve okuma kapasitesi, filtreleyip filtrelemediğine
bakılmaksızın aynıdır. Yani tam bir Scan üzerinde uygulama tarafı gruplama, her
toplamada tüm tabloyu okumaya ödediğin ve gecikmenin tabloyla büyüdüğü anlamına
gelir. AWS, "okuma zamanında tara ve say"ı "yalnızca gecikmenin sorun olmadığı çok
küçük veri kümeleri için uygun" olarak listeler
(AWS: Neden toplamaları önceden hesaplamalı).
Bir Query aracılığıyla tek bir partition'a daraltıldığında (örn. bir müşterinin
siparişlerini say), uygulama tarafı gruplama tamamen makuldür — yalnızca bir öğe
koleksiyonu okuyorsun. İkisi arasındaki tam maliyet boşluğu için bkz.
Query vs Scan. Belirli bir toplama taramasının
çalıştırmadan önce ne okuyacağını tahmin etmek için, temsili bir öğeyi
öğe boyutu hesaplayıcısı ile boyutlandır —
okuma kapasitesi her 4 KB'ta yukarı yuvarlanır,
dolayısıyla faturayı öğe boyutu belirler; sonra taramanın satır fiyatını
fiyatlandırma hesaplayıcısında çıkar.
Bir DynamoDB tablosu üzerinde gerçekten ad-hoc analitik SQL için — bir kez
çalıştıracağın "GROUP BY status, onları say" tarzı, kullan-at sorgu — AWS'nin
yanıtı ona ayrı bir motor yöneltmektir: Amazon Athena DynamoDB connector, bir
Lambda connector aracılığıyla tabloyu gerçek SQL (GROUP BY, toplamalar, hatta
başka kaynaklara JOIN'ler) ile sorgulamana izin verir
(AWS: DynamoDB için Amazon Athena bağlayıcısı).
Perde arkasında tabloyu tarar, dolayısıyla bir raporlama/BI aracıdır, sıcak bir yol
değil.
Hangi deseni kullanırım?
| İhtiyacın… | Kullan |
|---|---|
| Sıcak bir okuma yolunda bilinen bir grup toplamı | Desen 1 — atomik sayaç (ADD) |
| Yazma yoluna dokunmadan toplamalar | Desen 2 — Streams + Lambda rollup |
| Tek bir partition'a daraltılmış bir sayım | Desen 3 — Query sonra uygulamada grupla |
| Kesin toplamlar, kayma yok | Desen 1/2 idempotentlik koruması ile |
Keşif sırasında tek seferlik bir GROUP BY | DynoTable Workbench (aşağıda) veya Athena |
| SQL ile yinelenen BI/raporlama | Athena DynamoDB connector |
GROUP BY'ı doğrudan DynoTable'ın SQL Workbench'inde çalıştırma
Yukarıdaki desenler, toplamaları üretimde nasıl sunduğundur. Ama bir tabloyu keşfederken — "şu anda durum başına kaç sipariş var?" — bir Lambda tahsis etmek ya da Athena ayağa kaldırmak istemezsin. Sorguyu yazmak istersin.
DynoTable'ın SQL Workbench'i bunun içindir. Gerçek SQL çalıştırır —
GROUP BY, COUNT, SUM, AVG, HAVING, hatta JOIN — doğrudan canlı
DynamoDB tablolarına karşı, toplamayı okuduğu satırlar üzerinde istemci tarafında
yürüterek. Bu, DynamoDB'nin PartiQL endpoint'inin reddettiği SQL'dir:
SELECT status, COUNT(*) AS orders, SUM(total) AS revenue
FROM "Orders"
GROUP BY status
HAVING SUM(total) > 1000
ORDER BY revenue DESCkaputun altında DynoTable öğeleri API'nin izin verdiği şekilde
okur (yapabildiği yerde Query, mecbur olduğu yerde Scan), onları
somutlaştırır ve gruplamayı Workbench'te yapar — Desen 3 ile aynı "oku sonra
topla" mekaniği, sadece döngü olmadan ve DynamoDB'nin erişim modeli kurallarının
içinde. Keşif ve ad-hoc analiz için yapılmıştır, sıcak bir okuma yolunda bir
üretim rollup'ının yerini almak için değil. Onun için önceden hesapla (Desen 1 / 2).
Aynı kamanın JOIN tarafı için — DynoTable, PartiQL'in de yapamadığı tablolar-arası
join'ler çalıştırır — bkz. DynamoDB JOIN. Tam olarak bu
yetenekte GUI istemcilerini mi karşılaştırıyorsun? Bkz.
DynamoDB GUI karşılaştırması.
SSS
DynamoDB PartiQL, GROUP BY'ı destekler mi?
Hayır. DynamoDB'nin PartiQL SELECT'i yalnızca WHERE ve ORDER BY'ı destekler
— GROUP BY, HAVING, toplama fonksiyonları veya JOIN yok. Gramer,
SELECT … FROM … [WHERE …] [ORDER BY …] olarak
belgelenmiştir.
Tüm bir DynamoDB tablosu üzerinde COUNT(*) yapabilir miyim?
Bir toplama fonksiyonu olarak değil — PartiQL'in hiçbiri yoktur. API sana bir
Scan/Query üzerinde Select=COUNT verir; bu, eşleşen öğelerin bir sayısını
döndürür ama yine taramanın dokunduğu her öğeyi okur (ve faturalandırır)
(AWS Scan API referansı:
kapasite döndürülene değil, incelenen öğelere dayanır). Sık okunan bir toplam için,
bir sayaç öğesi tut (Desen 1).
Partition key'e göre GROUP BY yapabilir miyim?
DynamoDB'de veya PartiQL'de değil. "Partition key başına" bilinen bir erişim
deseniyse, anahtar başına bir atomik ADD ile bir toplama öğesi tut (Desen 1) ya
da Streams + Lambda ile rollup yap (Desen 2).
Grup başına SUM veya AVG'yi nasıl yaparım?
SUM: grup başına çalışan bir toplam tut ve yazmada ona ADD ekle. AVG: hem
toplamı hem sayımı sakla ve okuma zamanında böl — yerel bir ortalama yoktur. Tek
seferlik keşif amaçlı bir AVG için, onu DynoTable'ın SQL Workbench'inde ya da
Athena DynamoDB connector aracılığıyla çalıştır.
Bir partiql group by geçici çözümü var mı?
PartiQL tarafında bir tane yok. Ya toplamayı önceden hesapla (sayaçlar/Streams) ve
rollup öğesini SELECT et, ya da GROUP BY'ı ona sahip bir motorda çalıştır —
ad-hoc için DynoTable'ın Workbench'i, yinelenen raporlama için Athena.
Kendi tablolarına karşı bir Lambda yazmadan GROUP BY çalıştırmak mı istiyorsun?
DynoTable'ı dene ve SQL Workbench'i canlı bir tabloya yönelt.