Pemula8 menit baca

Cara COUNT, SUM, dan Agregasi di DynamoDB

DynamoDB memiliki 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 dapatkan membaca (dan menagih) setiap Item yang dihitungnya. Panduan ini mencakup apa yang sebenarnya didukung, pendekatan yang biasa diandalkan orang, dan cara menjalankan COUNT/SUM/AVG sungguhan atas sebuah tabel saat Anda membutuhkannya.

Bisakah DynamoDB melakukan SUM, COUNT, dan fungsi agregat?

Sebagian besar tidak. Satu-satunya agregat bawaan DynamoDB adalah Select=COUNT, yang mengembalikan jumlah Item yang cocok tetapi tetap membaca (dan menagih) setiap Item. Tidak ada SUM, AVG, MIN, atau MAX native, dan PartiQL juga tidak menambahkannya. Untuk agregat sungguhan dengan GROUP BY, lipat di aplikasi Anda, pelihara sebuah counter, atau jalankan SQL di SQL Workbench DynoTable.

  • Select=COUNT mengembalikan jumlah Item yang cocok, tetapi DynamoDB tetap membaca setiap Item untuk menghasilkannya — Anda membayar biaya pembacaan Scan/Query penuh, bukan biaya "count" yang murah.
  • Tidak ada SUM, AVG, MIN, atau MAX native. Operasi pembacaan DynamoDB mengembalikan Item; ia tidak melipatnya menjadi sebuah angka. PartiQL juga tidak menambahkan agregat.
  • DescribeTable.ItemCount gratis tetapi perkiraan dan diperbarui hanya "kira-kira setiap enam jam" — baik untuk ubin dashboard, salah untuk apa pun yang harus persis.
  • Untuk COUNT/SUM/AVG/MIN/MAX yang persis (dengan GROUP BY), agregasikan di aplikasi Anda, pelihara sebuah counter, atau jalankan di SQL Workbench DynoTable (di bawah).

Menghitung Item: Select=COUNT

Baik Query maupun Scan menerima parameter Select. Setel ke COUNT dan respons membawa jumlah alih-alih Item:

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

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

  • Count — "the number of items that remain, after a filter expression (if present) was applied."
  • ScannedCount — "the number of items evaluated, before any ScanFilter is applied." Tanpa filter, ScannedCount sama dengan Count.

Jika Anda hanya punya partition key dan perlu menghitung duplikat di dalamnya, condition + filter yang Anda berikan persis seperti yang dihasilkan DynamoDB Expression Builder — map KeyConditionExpression, FilterExpression, dan ExpressionAttributeNames/Values di atas — tanpa meng-escape JSON secara manual.

Dua jebakan lagi yang menggigit orang yang menghitung tabel besar:

  • Batas halaman 1 MB tetap berlaku. "If the size of the Scan result set is larger than 1 MB, ScannedCount and Count represent only a partial count of the total items" (dokumen AWS Scan). Anda harus melakukan paginasi dengan LastEvaluatedKeyExclusiveStartKey dan menyimpan total berjalan untuk mendapatkan angka yang sebenarnya — loop yang sama yang dibahas di paginasi DynamoDB.
  • Query yang sempit mengalahkan Scan. Select=COUNT pada sebuah Query hanya mengukur Item di partisi yang ditargetkan, bukan seluruh tabel. Jika Anda dapat menyematkan sebuah partition key (tabel dasar atau sebuah GSI), hitung di sana — itulah 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 pembacaan. Tangkapannya ada di referensi API itu sendiri: "DynamoDB updates this value approximately every six hours. Recent changes might not be reflected in this value." Jadi ia bisa tertinggal jauh di belakang kondisi sebenarnya tabel Anda.

Select=COUNTDescribeTable.ItemCount
KetepatanPersis (untuk set yang cocok)Perkiraan
KesegaranLiveDiperbarui ~setiap 6 jam
BiayaMembaca + menagih setiap Item yang dihitungGratis (metadata)
Bisa filter / hitung subsetYa (filter expression)Tidak — hanya seluruh tabel

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

Mengapa tidak ada SUM/AVG/MIN/MAX native

Operasi pembacaan DynamoDB mengembalikan Item. Tidak ada query planner untuk melipat sebuah result set menjadi skalar, jadi tidak ada apa pun untuk menghitung sebuah SUM atau AVG. Penghitungan adalah satu-satunya lipatan yang ditawarkan API, melalui Select=COUNT.

PartiQL tidak mengubah ini. Grammar SELECT PartiQL adalah SELECT {{expression}} [, …] FROM {{table}}[.{{index}}] [WHERE …] [ORDER BY {{key}} …], di mana expression adalah "a projection formed from the * wildcard or a projection list of one or more attribute names or document paths." Tidak ada fungsi agregat dan tidak ada klausa GROUP BY dalam grammar itu — dan ORDER BY menerima sebuah {{key}}, yang didokumentasikan sebagai "a hash key or a sort key to use to order returned results." Setiap SELECT PartiQL tetap dikompilasi menjadi sebuah GetItem, Query, atau Scan, jadi SELECT SUM(total) FROM "Orders" sama sekali tidak dapat dinyatakan. (Lebih lanjut tentang langit-langit PartiQL di PartiQL vs SQL.)

Pola agregasi (counter, stream, sisi-aplikasi)

Karena DynamoDB tidak akan mengagregasi untuk Anda, pola yang mapan mendorong pekerjaan itu ke tempat lain:

  • Item counter terpelihara. Pelihara sebuah Item khusus (mis. PK = "STATS#orders") dan ADD ke sebuah atribut numerik pada setiap penulisan dengan sebuah UpdateItem. Membaca agregatnya kemudian adalah sebuah GetItem tunggal — persis dan murah, tetapi Anda memiliki logika penambahan, konsistensinya, dan contention jika satu counter dihantam terus-menerus.
  • DynamoDB Streams → agregator. Aktifkan sebuah stream dan sambungkan ke sebuah Lambda yang memperbarui total berjalan (count, sum) saat Item berubah. Sesuai dokumen AWS Streams, Anda dapat mengonfigurasi StreamViewType stream sehingga setiap rekaman membawa NEW_AND_OLD_IMAGES — "both the new and the old images of the item" — cukup untuk menjaga agregat gaya-SUM tetap terkini tanpa men-scan ulang. Rekaman stream tunduk pada masa hidup 24 jam ("the stream records within a shard are removed automatically after 24 hours"), jadi konsumernya harus mengimbangi.
  • Lipatan sisi-aplikasi. Lakukan paginasi melalui Item yang cocok dan akumulasi SUM/AVG/MIN/MAX di kode Anda sendiri. Benar, tetapi ia membaca (dan menagih) setiap Item setiap kali — profil biaya yang sama dengan Select=COUNT, ditambah transfer datanya.
  • Alihkan ke analitik. Untuk agregasi analitis yang berat atau ad-hoc, ekspor tabel ke S3 dan query dengan Athena, atau alirkan ke sebuah warehouse. Sesuai dokumen AWS ekspor-ke-S3, mengekspor "doesn't consume read capacity units" dan memungkinkan Anda "perform analytics and complex queries using AWS services such as Athena" — jalur yang direkomendasikan AWS begitu Anda melampaui agregasi per-request.

Masing-masing menukar kesederhanaan dengan entah pembukuan saat-penulisan (counter, stream) atau biaya saat-pembacaan (scan sisi-aplikasi). Tidak 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 panduan tersendiri: DynamoDB GROUP BY.

Menjalankan COUNT/SUM/AVG di SQL Workbench DynoTable

Ketika Anda hanya butuh jawabannya — "berapa banyak order OPEN, dan berapa totalnya" — tanpa menulis loop scan berpaginasi atau sebuah Lambda, SQL Workbench DynoTable menjalankan agregat sungguhan. Ia memmaterialisasi tabel Anda melalui runtime Query/Scan DynamoDB yang sebenarnya, lalu menjalankan SQL Anda sepenuhnya di atasnya: SQL 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 — yang tidak satu pun dapat dinyatakan DynamoDB atau PartiQL — dalam satu pernyataan. Ini adalah irisan analitis yang sama dengan SQL untuk DynamoDB; untuk cerita pengelompokan lengkap lihat DynamoDB GROUP BY.

Workbench jujur tentang model akses di baliknya, bukan Postgres yang berpura-pura:

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

FAQ

Bisakah saya menghitung Item di DynamoDB tanpa men-scan? Tidak persis. Untuk count yang persis dan terkini Anda harus membaca Item — Select=COUNT tetap mengukur setiap Item yang dihitung. Satu-satunya opsi tanpa-scan adalah DescribeTable.ItemCount yang perkiraan (diperbarui ~setiap 6 jam) atau sebuah Item counter yang Anda pelihara sendiri pada setiap penulisan.

Bagaimana cara menghitung Item berdasarkan sebuah GSI? Jalankan Query (atau Scan) terhadap index dengan Select=COUNT. Menghitung melalui sebuah partisi GSI yang sempit jauh lebih murah daripada men-scan tabel dasar, karena Anda hanya membaca Item di partisi index itu — modelkan index di sekitar count yang Anda butuhkan.

Apakah DescribeTable.ItemCount akurat? Ia perkiraan. Referensi API menyatakan DynamoDB memperbarui ItemCount dan TableSizeBytes "approximately every six hours," dan "recent changes might not be reflected in this value." Jangan gunakan di tempat yang membutuhkan angka persis atau live.

Bisakah DynamoDB melakukan SUM atau AVG? Tidak secara native, dan tidak di PartiQL — grammar SELECT PartiQL tidak memiliki fungsi agregat. Agregasikan di aplikasi Anda, pelihara sebuah counter (opsional melalui 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 tidak ada filter expression. Celah besar di antara keduanya berarti count yang tidak efisien.


Perlu men-sum, merata-rata, atau mengelompokkan data DynamoDB Anda tanpa menulis loop scan? Unduh DynoTable dan jalankan di sebuah Tab Workbench. Membandingkan klien terlebih dahulu? Lihat di mana posisinya dibandingkan dengan GUI DynamoDB biasa.

Diperbarui