Pemula6 menit baca

Mengapa Scan DynamoDB lambat dan mahal

Scan membaca setiap item dalam tabel dan baru memfilter sesudahnya. Itu operasi yang Anda jangkau dari memori otot SQL, dan yang diam-diam menaikkan tagihan sambil membuat latensi lebih buruk daripada kotak RDS yang Anda tinggalkan.

Mengapa Scan DynamoDB saya lambat dan mahal?

Scan membaca setiap item dalam tabel sebelum FilterExpression berjalan, jadi Anda membayar untuk membaca seluruh tabel berapa pun sedikit baris yang kembali, dan ia semakin lambat saat tabel tumbuh. Perbaikannya hampir selalu Query ber-key — model access pattern di sekitar key sehingga DynamoDB menyentuh satu partisi alih-alih semuanya.

  • Scan membaca seluruh tabel, setiap kali. Ukuran, bukan jumlah hasil, memutuskan apa yang Anda bayar dan berapa lama.
  • FilterExpression bohong soal biaya. Ia berjalan setelah baca diukur, jadi mengembalikan 12 item bisa menagih baca 12 juta.
  • Scan semakin lambat saat Anda tumbuh. Query ber-key tetap datar — ia menyentuh satu partisi berapa pun besar tabelnya.
  • Perbaikannya hampir selalu pemodelan, bukan tuning. Jika Anda Scan untuk menjawab pertanyaan rutin, Anda kekurangan key.

Apa yang sebenarnya dilakukan Scan

Datang dari SQL, SELECT * FROM events WHERE type = 'checkout' terasa gratis — engine punya indeks, atau tidak, tetapi bagaimanapun Anda dapat baris. Di DynamoDB tidak ada query planner yang memutuskan itu untuk Anda.

Scan menelusuri seluruh tabel secara berurutan, 1 MB per kali, dan menyerahkan tiap halaman ke FilterExpression Anda. Apa pun yang ditolak filter tetap dibaca, tetap diukur, dan tetap di tagihan. (AWS: Scanning tables)

Itulah jebakannya. Filter terlihat seperti klausa WHERE, tetapi mengubah result set, bukan biaya. Scan mengonsumsi kapasitas baca yang sama ada atau tidaknya filter. (AWS: Scanning tables)

Hitung unit baca

DynamoDB mengukur baca dalam (RCU). Satu RCU membeli satu baca item hingga 4 KB; baca biayanya setengah. Item lebih besar dibulatkan ke 4 KB berikutnya. (AWS: Read/write capacity mode)

Ambil tabel analitik, ProductEvents. Tiap baris adalah satu event terlacak:

PK  = "TENANT#acme"
SK  = "TS#2026-06-23T14:08:55Z#evt_9f3a"
attrs: eventType, sessionId, userId, payloadBytes

Katakan ia menampung 2.000.000 event, masing-masing ~1 KB, semua di bawah satu tenant sibuk. Anda ingin checkout hari ini. Gerakan refleks:

Scan ProductEvents
FilterExpression: eventType = "checkout"

Filter itu mungkin mengembalikan 40 baris. Tetapi Scan membaca semua 2.000.000 item dulu. Pada ~1 KB masing-masing (1 RCU per 4 KB, eventually consistent ≈ 0,5 RCU per 4 KB), Anda mengukur kira-kira 250.000 RCU — dan memaginasi ~2 GB data — untuk menyerahkan 40 item.

Sekarang model access pattern sebagai key dan Query sebagai gantinya:

Query ProductEvents
PK = "TENANT#acme"
AND SK begins_with "TS#2026-06-23"

Ini hanya membaca irisan yang cocok dari satu partisi. Jika 40 baris checkout itu plus event lain hari itu mencapai ~2 MB, Anda membayar ~2 MB baca, bukan 2 GB. Jawaban sama, sepecahan biaya — dan latensi tetap datar saat tabel tumbuh.

Scan vs Query, diukur

Scan + filterQuery ber-key
MembacaSetiap item di tabelSatu partisi, dipersempit SK
Kapasitas ditagihSeluruh tabel, sebelum filterHanya item di irisan Anda
Contoh kita~250.000 RCU (~2 GB)beberapa ratus RCU (~2 MB)
LatensiTumbuh dengan ukuran tabelDatar saat tabel tumbuh
Jumlah hasilTidak memutuskan apa pun soal biayaCocok dengan apa yang Anda bayar

Pada Scan, jumlah hasil dan tagihan Anda tidak berhubungan. Pada Query, mereka saling mengikuti.

Putuskan sebelum Scan

Sebagian besar Scan tak sengaja datang dari satu pertanyaan: bisakah saya menamai partisi yang saya butuhkan? Jika ya, itu Query. Jika tidak, perbaikannya adalah key, bukan filter lebih besar.

Berikut keputusan dalam bentuk alur.

YaTidakYaTidakPerlu membaca itemTahu partition key?Query satu partisiBisakah GSI memberi key?Tambah GSI, lalu QueryScan pilihan terakhir

Jalur hampir selalu berakhir di Query; Anda hanya jatuh ke Scan ketika tidak ada key — yang ada atau yang bisa ditambahkan — yang cocok dengan access pattern.

Jika pattern nyata dan berulang tetapi tabel dasar tidak bisa memberinya key, itu sinyal untuk menambah Global Secondary Index agar pertanyaan menjadi Query. Memodelkan key di sekitar access pattern di depan adalah seluruh permainannya — lihat single-table design.

Tulis query ber-key, bukan filter

Saat Anda butuh kondisi di luar key, bangun dengan sengaja alih-alih membuang semuanya ke FilterExpression. DynamoDB Expression Builder menghasilkan KeyConditionExpression dan placeholder atribut untuk Anda, sehingga partition key dan sort key yang mempersempit — sebelum DynamoDB mengukur baca, bukan sesudah.

KeyConditionExpression: PK = :tenant AND begins_with(SK, :day)

Kapan Scan benar-benar boleh

Scan adalah default yang salah untuk query rutin. Itu alat yang tepat saat Anda benar-benar bermaksud "baca semuanya":

  • Ekspor sekali atau backfill dijalankan manual.
  • Tabel config / lookup kecil di mana seluruh tabel hanya beberapa KB.
  • Job latar yang sengaja memaginasi seluruh tabel. Bagi lintas worker dengan Segment / TotalSegments — alih-alih satu crawl berurutan panjang. (AWS: Scanning tables)

Dan catat PartiQL tidak menyelamatkan Anda: SELECT * FROM ProductEvents WHERE eventType = 'checkout' tanpa predikat key dikompilasi langsung menjadi Scan. Jebakan yang sama berbaju SQL. (Lihat Query vs Scan untuk rincian API lengkap.)

Saat Anda benar-benar butuh analitik lintas-item — GROUP BY, JOIN, agregat yang DynamoDB tidak bisa ekspresikan — SQL Workbench DynoTable menjalankannya di sisi klien atas result set terbatas, alih-alih menghantam tabel dengan Scan penuh.

Langkah selanjutnya

Perkirakan biaya kedua pattern dengan kalkulator harga, baca Query vs Scan untuk kontras tingkat API, dan unduh DynoTable untuk menjalankannya terhadap tabel Anda sendiri dan melihat berapa banyak item yang sebenarnya dibaca tiap pendekatan.

Diperbarui