Pemula8 menit baca

Cara COUNT, SUM dan Agregasi di DynamoDB

DynamoDB punya tepat satu agregat bawaan: menghitung item yang cocok dengan Select=COUNT. Tidak ada SUM, AVG, MIN, atau MAX native. Dan bahkan count yang bisa Anda dapat membaca (dan menagih) setiap item yang ia hitung. Panduan ini membahas apa yang sebenarnya didukung, aproksimasi yang orang jangkau, dan bagaimana menjalankan COUNT/SUM/AVG nyata atas sebuah tabel saat Anda membutuhkannya.

Bisakah DynamoDB melakukan SUM, COUNT, dan fungsi agregat?

Kebanyakan tidak. Satu-satunya agregat bawaan DynamoDB adalah Select=COUNT, yang mengembalikan hitungan item-yang-cocok tapi tetap membaca (dan menagih) setiap item. Tak ada SUM, AVG, MIN, atau MAX native, dan PartiQL pun tak menambahkan apa pun. Untuk agregat nyata dengan GROUP BY, lipat ia di aplikasi Anda, pelihara sebuah counter, atau jalankan SQL di Workbench DynoTable.

  • Select=COUNT mengembalikan jumlah item yang cocok, tapi DynamoDB tetap membaca setiap item untuk menghasilkannya — Anda membayar biaya baca Scan/Query penuh, bukan sebuah biaya "hitung" yang murah.
  • Tak ada SUM, AVG, MIN, atau MAX native. Operasi baca DynamoDB mengembalikan item; mereka tak melipatnya menjadi sebuah angka. PartiQL pun tak menambahkan agregat.
  • DescribeTable.ItemCount gratis tapi aproksimatif dan diperbarui hanya "kira-kira setiap enam jam" — cukup untuk sebuah petak dasbor, salah untuk apa pun yang eksak.
  • Untuk COUNT/SUM/AVG/MIN/MAX eksak (dengan GROUP BY), agregasi di aplikasi Anda, pelihara sebuah counter, atau jalankan ia di SQL Workbench DynoTable (di bawah).

Menghitung item: Select=COUNT

Baik Query maupun Scan menerima sebuah parameter Select. Setel ia ke COUNT dan responsnya membawa hitungannya alih-alih item-item:

aws dynamodb scan \
  --table-name Orders \
  --select COUNT \
  --filter-expression "#s = :open" \
  --expression-attribute-names '{"#s":"status"}' \
  --expression-attribute-values '{":open":{"S":"OPEN"}}'

Responsnya memberi Anda dua angka (AWS: Counting the items in the results):

  • Count — "jumlah item yang tersisa, setelah sebuah filter expression (jika ada) diterapkan."
  • ScannedCount — "jumlah item yang dievaluasi, sebelum ScanFilter mana pun diterapkan." Tanpa filter, ScannedCount sama dengan Count.

Jika Anda hanya punya dan perlu menghitung duplikat di dalamnya, kondisi + filter yang Anda berikan persis apa yang dihasilkan DynamoDB Expression Builder — peta FilterExpression dan ExpressionAttributeNames/Values di atas, plus KeyConditionExpression saat Anda menghitung di dalam satu partisi via Query — tanpa meng-escape JSON dengan tangan.

Dua jebakan lagi yang menggigit orang yang menghitung tabel besar:

  • Batas halaman 1 MB tetap berlaku. "Jika ukuran set hasil Scan lebih besar dari 1 MB, ScannedCount dan Count hanya merepresentasikan hitungan sebagian dari total item" (dokumen AWS Scan). Anda harus mempaginasi dengan memberi umpan balik LastEvaluatedKey setiap respons sebagai ExclusiveStartKey permintaan berikutnya, menjaga total berjalan untuk mendapatkan angka sebenarnya — loop yang sama yang dibahas di pagination DynamoDB.
  • Sebuah Query yang sempit mengalahkan sebuah Scan. Select=COUNT pada sebuah Query memeter hanya item di partisi yang ditargetkan, bukan seluruh tabel. Jika Anda bisa menyematkan sebuah partition key (tabel dasar atau sebuah GSI), hitung di sana — itu adalah celah biaya Query-vs-Scan yang diterapkan pada penghitungan.

Select=COUNT vs ItemCount (dan mengapa ia basi)

DescribeTable mengembalikan sebuah ItemCount (dan TableSizeBytes) secara gratis, tanpa biaya baca. Jebakannya ada di referensi API itu sendiri: "DynamoDB memperbarui nilai ini kira-kira setiap enam jam. Perubahan terbaru mungkin tidak tercermin dalam nilai ini." Jadi ia bisa tertinggal jauh di belakang keadaan aktual tabel Anda.

Select=COUNTDescribeTable.ItemCount
KeeksakanEksak (untuk set yang cocok)Aproksimatif
KesegaranLiveDiperbarui ~setiap 6 jam
BiayaMembaca + menagih setiap item yang dihitungGratis (metadata)
Bisa memfilter / menghitung subsetYa (filter expression)Tidak — seluruh tabel saja

Gunakan ItemCount untuk sebuah cek kasar "seberapa besar tabel ini" atau sebuah petak dasbor. Gunakan Select=COUNT saat Anda butuh sebuah angka yang eksak, terfilter, atau terkini — dan terima biaya bacanya. Untuk apa pun yang benar-benar live dan gratis, lacak sebuah counter sendiri (lihat Pola agregasi di bawah).

Mengapa tak ada SUM/AVG/MIN/MAX native

Operasi baca DynamoDB mengembalikan item. Tak ada query planner untuk melipat sebuah set hasil menjadi sebuah skalar, jadi tak ada apa pun untuk menghitung sebuah SUM atau AVG. Penghitungan adalah satu-satunya lipatan yang ditawarkan API, via Select=COUNT.

PartiQL tak mengubah ini. Tata bahasa SELECT PartiQL adalah SELECT {{expression}} [, …] FROM {{table}}[.{{index}}] [WHERE …] [ORDER BY {{key}} …], di mana ekspresinya adalah "sebuah proyeksi yang dibentuk dari wildcard * atau sebuah daftar proyeksi berisi satu atau lebih nama atribut atau path dokumen." Tak ada fungsi agregat dan tak ada klausa GROUP BY di tata bahasa itu — dan ORDER BY mengambil sebuah {{key}}, didokumentasikan sebagai "sebuah hash key atau sebuah sort key untuk digunakan mengurutkan hasil yang dikembalikan." Setiap SELECT PartiQL tetap dikompilasi menjadi sebuah GetItem, Query, atau Scan, jadi SELECT SUM(total) FROM "Orders" sekadar tak bisa diekspresikan. (Lebih lanjut tentang batas PartiQL di PartiQL vs SQL.)

Pola agregasi (counter, stream, sisi-aplikasi)

Karena DynamoDB tak akan mengagregasi untuk Anda, pola-pola yang mapan mendorong pekerjaannya ke tempat lain:

  • Item counter yang dipelihara. Simpan sebuah item khusus (mis. PK = "STATS#orders") dan ADD ke sebuah atribut numerik pada setiap penulisan dengan sebuah UpdateItem. Membaca agregatnya lalu menjadi satu GetItem — eksak dan murah, tapi Anda memiliki logika penambahannya, konsistensinya, dan kontensi jika satu counter dihajar.
  • memberi umpan sebuah aggregator. Aktifkan sebuah stream dan sambungkan ia ke sebuah Lambda yang memperbarui total berjalan (hitungan, sum) saat item berubah. Menurut dokumen AWS Streams, Anda bisa mengonfigurasi StreamViewType stream-nya sehingga setiap rekaman membawa NEW_AND_OLD_IMAGES — "baik image baru maupun lama dari item" — cukup untuk menjaga agregat gaya-SUM tetap terkini tanpa memindai ulang. Rekaman stream tunduk pada masa hidup 24 jam ("rekaman stream di dalam sebuah shard dihapus secara otomatis setelah 24 jam"), jadi konsumernya harus mengikuti.
  • Lipat sisi-aplikasi. Halaman demi halaman melalui item yang cocok dan akumulasikan SUM/AVG/MIN/MAX di kode Anda sendiri. Benar, tapi ia membaca (dan menagih) setiap item setiap kali — profil biaya yang sama dengan Select=COUNT, plus transfer datanya.
  • Alihkan ke analitik. Untuk agregasi analitik yang berat atau ad-hoc, ekspor tabelnya ke S3 dan query ia dengan Athena, atau alirkan ia ke sebuah warehouse. Menurut dokumen AWS ekspor-ke-S3, mengekspor "tidak menghabiskan read capacity unit" dan membiarkan Anda "melakukan analitik dan query kompleks menggunakan layanan AWS seperti Athena" — jalur yang direkomendasikan AWS begitu Anda melampaui agregasi per-permintaan.

Masing-masing menukar kesederhanaan dengan entah pembukuan waktu-tulis (counter, stream) atau biaya waktu-baca (scan sisi-aplikasi). Tak ada pola yang membuat DynamoDB sendiri menghitung sebuah SUM secara gratis. Versi pengelompokan dari tradeoff ini — mengagregasi per key alih-alih atas seluruh tabel — adalah panduannya sendiri: DynamoDB GROUP BY.

Menjalankan COUNT/SUM/AVG di SQL Workbench DynoTable

Saat Anda hanya butuh jawabannya — "berapa banyak order OPEN, dan berapa totalnya" — tanpa menulis sebuah loop scan yang mempaginasi atau sebuah Lambda, SQL Workbench DynoTable menjalankan agregat nyata. Ia mematerialisasi tabel Anda melalui runtime Query/Scan aktual DynamoDB, lalu menjalankan satu SELECT di atasnya — agregat, GROUP BY, HAVING, DISTINCT: SQL di dalam aturan pola-akses DynamoDB.

-- Runs in the DynoTable Workbench (NOT in PartiQL):
SELECT status,
       COUNT(*)        AS orders,
       SUM(total)      AS revenue,
       AVG(total)      AS avg_order,
       MIN(total)      AS smallest,
       MAX(total)      AS largest
FROM orders
GROUP BY status
ORDER BY revenue DESC

Itu COUNT, SUM, AVG, MIN, MAX, GROUP BY, dan ORDER BY pada sebuah agregat terhitung — tak satu pun yang bisa diekspresikan DynamoDB atau PartiQL (ORDER BY PartiQL terbatas pada atribut key) — dalam satu pernyataan. Ini adalah wedge analitik yang sama seperti SQL untuk DynamoDB; untuk cerita pengelompokan lengkap lihat DynamoDB GROUP BY.

Workbench jujur tentang model akses di bawahnya, bukan sebuah pura-pura-Postgres:

  • Baris-barisnya tetap datang melalui Query/Scan nyata DynamoDB. Sebuah GROUP BY atas seluruh tabel tetap sebuah Scan di bawahnya — Workbench memunculkan biaya itu alih-alih menyembunyikannya, tradeoff Query-vs-Scan yang sama.
  • Agregat berjalan pada atribut skalar yang dimaterialisasi setelah baris-barisnya mendarat.

FAQ

Bisakah saya menghitung item di DynamoDB tanpa memindai? Tak persis. Untuk sebuah hitungan yang eksak dan terkini Anda harus membaca item-nya — Select=COUNT tetap memeter setiap item yang dihitung. Satu-satunya opsi tanpa-scan adalah DescribeTable.ItemCount yang aproksimatif (diperbarui ~setiap 6 jam) atau sebuah item counter yang Anda pelihara sendiri pada setiap penulisan.

Bagaimana saya menghitung item menurut sebuah GSI? Jalankan Query (atau Scan) terhadap index dengan Select=COUNT. Menghitung via sebuah partisi GSI yang sempit jauh lebih murah daripada memindai tabel dasar, karena Anda hanya membaca item di partisi index itu — modelkan index-nya di sekitar hitungan yang Anda butuhkan.

Apakah DescribeTable.ItemCount akurat? Ia aproksimatif. Referensi API menyatakan DynamoDB memperbarui ItemCount dan TableSizeBytes "kira-kira setiap enam jam," dan "perubahan terbaru mungkin tidak tercermin dalam nilai ini." Jangan gunakan ia di mana sebuah angka eksak atau live penting.

Bisakah DynamoDB melakukan SUM atau AVG? Tidak secara native, dan tidak di PartiQL — tata bahasa SELECT PartiQL tak punya fungsi agregat. Agregasi di aplikasi Anda, pelihara sebuah counter (opsional via DynamoDB Streams), atau jalankan SUM/AVG di SQL Workbench DynoTable.

Apa perbedaan antara Count dan ScannedCount? ScannedCount adalah berapa banyak item yang dievaluasi DynamoDB sebelum filter Anda; Count adalah berapa banyak yang tersisa setelahnya. Keduanya sama saat tak ada filter expression. Sebuah celah besar di antaranya berarti sebuah hitungan yang tidak efisien.


Perlu menjumlah, merata-rata, atau mengelompokkan data DynamoDB Anda tanpa menulis sebuah loop scan? Unduh DynoTable dan jalankan ia di sebuah tab Workbench. Membandingkan klien dulu? Lihat di mana ia berdiri terhadap sebuah GUI DynamoDB biasa.

Diperbarui