Query vs Scan di DynamoDB
Query membaca satu koleksi Item berdasarkan partition key (opsional mempersempit
sort key); Scan membaca seluruh tabel dan memfilter sesudahnya. Keduanya
terlihat mirip di API tetapi keduanya menagih — dan menskala — secara benar-benar
berbeda.
Kapan sebaiknya saya menggunakan Query vs Scan di DynamoDB?
Gunakan Query setiap kali Anda dapat menyebutkan partisi yang Anda butuhkan — ia membaca satu koleksi Item dan hanya menagih untuk Item yang cocok. Gunakan Scan hanya untuk ekspor sekali pakai atau tabel kecil; ia membaca setiap Item dan menagih seluruh tabel sebelum FilterExpression apa pun berjalan. Pada data nyata, Query selalu menang.
- Query bersifat tertarget: Anda membayar untuk Item dalam partisi yang cocok.
- Scan bersifat menyeluruh: Anda membayar untuk membaca setiap Item, lalu
membuang sebagian besar dengan
FilterExpressionyang berjalan setelah pembacaan dihitung.
Pada tabel berukuran nyata apa pun, sebuah Scan dengan filter adalah jebakan
klasik "kenapa tagihan saya besar dan latensi saya lebih buruk daripada RDS".
Berdampingan
| Query | Scan | |
|---|---|---|
| Pembacaan | Satu partisi (berdasarkan PK) | Setiap Item dalam tabel |
| Kapasitas ditagih | Item yang cocok dalam partisi | Seluruh tabel, sebelum pemfilteran |
FilterExpression | Diterapkan setelah pembacaan — tetap ditagih untuk pembacaan | Sama — pemfilteran tak pernah memangkas biaya |
| Latensi | Datar seiring tabel bertumbuh | Bertumbuh seiring ukuran tabel |
| Paginasi | 1 MB/halaman → LastEvaluatedKey | 1 MB/halaman; dapat diparalelkan |
| Gunakan untuk | Pola akses yang diketahui | Ekspor sekali pakai, tabel config kecil |
Jebakan utama: sebuah FilterExpression berjalan setelah DynamoDB menghitung
pembacaan, pada kedua operasi. Sebuah Scan yang "mengembalikan 10 baris" dapat
menagih untuk membaca satu juta — pemfilteran adalah kenyamanan, bukan pengendali
biaya.
Berapa biaya sebenarnya sebuah Scan penuh
Mari beri angka. DynamoDB me-meter pembacaan dalam unit 4 KB: sebuah pembacaan
berbiaya satu
per 4 KB, sebuah pembacaan
setengahnya. Query dan
Scan menjumlahkan ukuran setiap Item yang mereka sentuh — bukan setiap
Item yang mereka kembalikan — dan membulatkannya ke atas ke 4 KB berikutnya.
Ambil sebuah tabel 1 juta Item dengan rata-rata 2 KB per Item (~2 GB data), dan sebuah pola akses yang membutuhkan 10 dari Item-item itu:
| Item dibaca | Data di-meter | Read unit (eventually consistent) | |
|---|---|---|---|
Scan + FilterExpression | 1,000,000 | ~2 GB | ~262,000 |
Query pada key yang cocok | 10 | 20 KB | 3 |
Sepuluh Item yang sama, terpaut lima orde besaran — dan Scan menagih ~262,000
itu setiap kali ia berjalan, entah filternya cocok dengan sepuluh Item atau
tidak satu pun. Pada penagihan itu adalah request unit
yang langsung masuk tagihan; pada tabel sebuah Scan
besar bersaing dengan trafik produksi memperebutkan throughput dan dapat
men-throttle-nya menjadi
ProvisionedThroughputExceededException.
Tiga fakta biaya lagi yang mengejutkan banyak orang:
Select: COUNTtidak gratis. Sebuah Query atau Scan yang menghitung mengonsumsi kapasitas baca yang persis sama dengan membaca Item-nya — ia hanya tidak mengembalikannya.Limitmembatasi Item yang dievaluasi, bukan Item yang cocok. Dikombinasikan dengan sebuah filter, sebuah halaman bisa kembali kosong sambil tetap menagih satu halaman penuh pembacaan.- Anda tak pernah harus menebak. Kirimkan
ReturnConsumedCapacity: TOTALdan setiap respons melaporkan kapasitas yang baru saja dikonsumsinya.
Periksa berapa berat Item Anda sendiri dengan kalkulator ukuran item, lalu ubah read unit menjadi tagihan bulanan dengan kalkulator harga.
Gunakan Query
Query PK = "USER#42" AND SK begins_with "ORDER#"
Jika Anda mendapati diri Anda meraih Scan untuk menjawab pola akses yang umum,
itu adalah sinyal pemodelan: tambahkan
Global Secondary Index sehingga pola itu menjadi sebuah
Query.
Pilihannya bermuara pada satu pertanyaan — bisakah Anda menyebutkan partisi yang Anda butuhkan?
Jika key-nya diketahui, Anda Query; jika tidak, tambahkan GSI untuk
menjadikannya satu, dan kembali ke Scan hanya saat tidak ada key yang cocok.
Kapan Scan baik-baik saja
Ekspor sekali pakai, tabel config kecil, dan job latar belakang yang mem-paging
seluruh tabel secara sengaja. Gunakan Segment/TotalSegments untuk membagi
sebuah Scan di antara worker (sebuah —
lihat parallel Scan di DynamoDB) saat Anda
benar-benar harus membaca semuanya, dan paginasikan dengan benar lewat
LastEvaluatedKey (panduan paginasi). Jika sebuah Scan
yang sudah Anda jalankan adalah masalahnya,
kenapa Scan lambat dan mahal memandu
triasenya.
Sebuah SELECT * FROM table refleksif atas DynamoDB adalah anti-pola yang sama
dalam balutan PartiQL — ia dikompilasi menjadi sebuah Scan. Saat Anda benar-benar
membutuhkan analitik lintas Item (sebuah GROUP BY, sebuah JOIN, sebuah agregat),
SQL Workbench DynoTable menjalankannya di sisi klien atas kumpulan hasil yang
terbatas alih-alih menggempur tabel.
Coba DynoTable untuk menjalankan dan memeriksa query-query ini terhadap tabel Anda sendiri — ia menampilkan kapasitas yang dikonsumsi setiap operasi yang dijalankannya.