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.
Scanmembaca seluruh tabel, setiap kali. Ukuran, bukan jumlah hasil, memutuskan apa yang Anda bayar dan berapa lama.FilterExpressionbohong soal biaya. Ia berjalan setelah baca diukur, jadi mengembalikan 12 item bisa menagih baca 12 juta.Scansemakin lambat saat Anda tumbuh.Queryber-key tetap datar — ia menyentuh satu partisi berapa pun besar tabelnya.- Perbaikannya hampir selalu pemodelan, bukan tuning. Jika Anda
Scanuntuk 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, payloadBytesKatakan 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 + filter | Query ber-key | |
|---|---|---|
| Membaca | Setiap item di tabel | Satu partisi, dipersempit SK |
| Kapasitas ditagih | Seluruh tabel, sebelum filter | Hanya item di irisan Anda |
| Contoh kita | ~250.000 RCU (~2 GB) | beberapa ratus RCU (~2 MB) |
| Latensi | Tumbuh dengan ukuran tabel | Datar saat tabel tumbuh |
| Jumlah hasil | Tidak memutuskan apa pun soal biaya | Cocok 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.
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.