DynamoDB Scan Neden Yavaş ve Pahalıdır
Scan tablodaki her öğeyi okur ve ancak sonra filtreler. SQL kas hafızasıyla
uzandığın operasyondur; faturanı sessizce şişirirken gecikmeyi bıraktığın RDS
kutusundan daha kötü hale getirir.
DynamoDB Scan'im neden yavaş ve pahalı?
Scan, FilterExpression çalışmadan önce tablodaki her öğeyi okur — kaç satır
dönerse dönsün tüm tabloyu okumak için ödersin ve tablo büyüdükçe yavaşlar.
Çözüm neredeyse her zaman anahtarlı bir Query'dir: erişim desenini bir anahtarın
etrafında modelle ki DynamoDB her şeye değil tek bir partition'a dokunsun.
Scanher seferinde tüm tabloyu okur. Ödediğin ve ne kadar sürdüğü sonuç sayına değil boyuta bağlıdır.FilterExpressionmaliyet konusunda yalan söyler. Okuma ölçüldükten sonra çalışır; 12 öğe döndürmek 12 milyon okuma faturalandırabilir.Scanbüyüdükçe yavaşlar. Anahtarlı birQuerydüz kalır — tablo ne kadar büyük olursa olsun tek bir partition'a dokunur.- Çözüm neredeyse her zaman ayar değil modellemedir. Rutin bir soruyu
Scanile yanıtlıyorsan bir anahtar eksik demektir.
Scan gerçekte ne yapar
SQL'den geliyorsan SELECT * FROM events WHERE type = 'checkout' bedava gibi
gelir — motorun indeksi vardır ya da yoktur, yine de satır alırsın. DynamoDB'de
bunu senin yerine karar veren bir query planner yok.
Scan tüm tabloyu sırayla gezer, her seferinde 1 MB, ve her sayfayı
FilterExpression'ına verir. Filtrenin reddettiği yine okunur, yine ölçülür,
yine faturandadır. (AWS: Scanning tables)
Tuzak budur. Filtre bir WHERE gibi görünür ama sonuç kümesini değiştirir,
maliyeti asla. Filtre olsun olmasın Scan aynı okuma kapasitesini tüketir.
(AWS: Scanning tables)
Okuma birimlerini say
DynamoDB okumaları (RCU) ile ölçer. Bir RCU, 4 KB'a kadar bir öğenin tek bir okumasını alır; okumalar bunun yarısıdır. Daha büyük öğeler bir sonraki 4 KB'a yuvarlanır. (AWS: Read/write capacity mode)
Bir analytics tablosu al, ProductEvents. Her satır izlenen bir olay:
PK = "TENANT#acme"
SK = "TS#2026-06-23T14:08:55Z#evt_9f3a"
attrs: eventType, sessionId, userId, payloadBytesDiyelim 2.000.000 olay tutuyor, her biri ~1 KB, hepsi meşgul bir tenant altında. Bugünün checkout'larını istiyorsun. Refleks hareket:
Scan ProductEvents
FilterExpression: eventType = "checkout"
Bu filtre belki 40 satır döner. Ama Scan önce 2.000.000 öğenin hepsini okudu.
Herbiri ~1 KB iken (4 KB başına 1 RCU, nihai tutarlı ≈ 4 KB başına 0,5 RCU)
kabaca 250.000 RCU ölçtün — ~2 GB veriyi sayfalayıp 40 öğe verdin.
Şimdi erişim desenini bir anahtar olarak modelle ve Query et:
Query ProductEvents
PK = "TENANT#acme"
AND SK begins_with "TS#2026-06-23"
Bu yalnızca bir partition'ın eşleşen dilimini okur. O 40 checkout satırı artı günün diğer olayları ~2 MB ise ~2 MB okuma ödersin, 2 GB değil. Aynı cevap, maliyetin küçük bir kesri — ve tablo büyürken gecikme düz kalır.
Scan vs Query, ölçülmüş
| Scan + filtre | Anahtarlı Query | |
|---|---|---|
| Okumalar | Tablodaki her öğe | Bir partition, SK ile daraltılmış |
| Faturalanan | Filtre öncesi tüm tablo | Yalnızca dilimindeki öğeler |
| Örneğimiz | ~250.000 RCU (~2 GB) | birkaç yüz RCU (~2 MB) |
| Gecikme | Tablo boyutuyla büyür | Tablo büyürken düz |
| Sonuç sayısı | Maliyet hakkında hiçbir şey söylemez | Ödediğinle örtüşür |
Bir Scan'de sonuç sayın ile faturan birbirinden bağımsızdır. Bir Query'de
birbirini takip ederler.
Scan etmeden önce karar ver
Çoğu kazara Scan, tek bir sorudan gelir: ihtiyacım olan partition'ı
adlandırabilir miyim? Evetse bu bir Query'dir. Hayırsa çözüm daha büyük bir
filtre değil, bir anahtardır.
Karar akış olarak şöyle:
Yol neredeyse her zaman Query'de biter; Scan'e yalnızca hiçbir anahtar —
mevcut veya eklenebilir — erişim desenine uymadığında düşersin.
Desen gerçek ve tekrarlanıyorsa ama base table bunu anahtarlayamıyorsa, bu bir
Global Secondary Index ekleme sinyalidir ki soru bir
Query olsun. Anahtarlarını erişim desenlerinin etrafında modellemek oyunun
tamamı — bak single-table design.
Filtre değil, anahtarlı sorguyu yaz
Anahtarın ötesinde bir koşula ihtiyacın olduğunda her şeyi bir
FilterExpression'a dökmek yerine bilinçli kur. DynamoDB Expression
Builder KeyConditionExpression ve
attribute placeholder'larını üretir; böylece partition ve sort key daraltmayı
yapar — DynamoDB okumayı ölçmeden önce, sonra değil.
KeyConditionExpression: PK = :tenant AND begins_with(SK, :day)
Scan ne zaman gerçekten uygundur
Scan rutin sorgular için yanlış varsayılandır. Gerçekten "her şeyi oku"
demek istediğinde doğru araçtır:
- Elle çalışan tek seferlik export veya backfill'ler.
- Tüm tablonun birkaç KB olduğu küçük config / lookup tabloları.
- Bilerek tüm tabloyu sayfalayan arka plan işleri. Bunları tek uzun sıralı
tarama yerine
Segment/TotalSegmentsile worker'lara böl — bir . (AWS: Scanning tables)
Ve PartiQL seni kurtarmaz: anahtar predicate'i olmayan SELECT * FROM ProductEvents WHERE eventType = 'checkout' doğrudan bir Scan'e derlenir. SQL
kıyafetinde aynı tuzak. (Tam ayrım için Query vs Scan.)
Gerçekten öğeler arası analytics gerektiğinde — DynamoDB'nin ifade edemediği bir
GROUP BY, JOIN veya aggregate — DynoTable'ın SQL Workbench'i bunları tam bir
Scan ile tabloyu dövmek yerine sınırlı bir sonuç kümesi üzerinde istemci tarafında
çalıştırır.
Sonraki adımlar
Her iki desenin maliyetini pricing calculator ile tahmin et, API düzeyindeki karşıtlık için Query vs Scan oku ve kendi tablolarına karşı çalıştırıp her yaklaşımın gerçekte kaç öğe okuduğunu görmek için DynoTable'ı indir.