Pemula5 menit baca

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 FilterExpression yang 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

QueryScan
PembacaanSatu partisi (berdasarkan PK)Setiap Item dalam tabel
Kapasitas ditagihItem yang cocok dalam partisiSeluruh tabel, sebelum pemfilteran
FilterExpressionDiterapkan setelah pembacaan — tetap ditagih untuk pembacaanSama — pemfilteran tak pernah memangkas biaya
LatensiDatar seiring tabel bertumbuhBertumbuh seiring ukuran tabel
Paginasi1 MB/halaman → LastEvaluatedKey1 MB/halaman; dapat diparalelkan
Gunakan untukPola akses yang diketahuiEkspor 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 dibacaData di-meterRead unit (eventually consistent)
Scan + FilterExpression1,000,000~2 GB~262,000
Query pada key yang cocok1020 KB3

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: COUNT tidak gratis. Sebuah Query atau Scan yang menghitung mengonsumsi kapasitas baca yang persis sama dengan membaca Item-nya — ia hanya tidak mengembalikannya.
  • Limit membatasi 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: TOTAL dan 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?

YesNoYesNoAccess patternPartition key known?Query reads one partitionCan a GSI key it?Add a GSIScan reads the whole table

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.

Diperbarui