Orta8 dakikalık okuma

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 SELECT grameri SELECT … FROM … [WHERE …] [ORDER BY …]'dir — ve bütün liste bu. GROUP BY yok, HAVING yok, toplama fonksiyonu yok, JOIN yok (AWS PartiQL SELECT referansı).
  • DynamoDB "öğeler arasında SUM veya COUNT gibi 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):

  1. Uygulama ham öğeyi yazar (bir sipariş, bir indirme, bir olay). Toplama mantığı yok.
  2. , yazmayı bir akış kaydı olarak yakalar.
  3. Akışa bağlı bir Lambda, yeni öğeyi okur, grubu türetir (durum, ay, kategori…) ve atomik bir UpdateItem ile eşleşen toplama öğesine ADD ekler — ki bu, birçok çağrı aynı sayaca dokunduğunda "oku-değiştir-yaz yarış koşullarından kaçınır".
  4. Önceden hesaplanmış toplamayı sorgularsın — genellikle yalnızca rollup öğelerini indeksleyen bir aracılığıyla, böylece "bu ay ilk 10", Limit 10 ile tek bir Query olur.

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 toplamalarDesen 2 — Streams + Lambda rollup
Tek bir partition'a daraltılmış bir sayımDesen 3 — Query sonra uygulamada grupla
Kesin toplamlar, kayma yokDesen 1/2 idempotentlik koruması ile
Keşif sırasında tek seferlik bir GROUP BYDynoTable Workbench (aşağıda) veya Athena
SQL ile yinelenen BI/raporlamaAthena 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 DESC

kaputun 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.

Güncellendi