Bir DynamoDB Scan Neden Yavaş ve Pahalıdır
Bir Scan, tablodaki her öğeyi okur ve yalnızca sonradan filtreler. SQL kas
hafızasından uzandığın işlemdir ve geride bıraktığın RDS kutusundan daha kötü bir
gecikme yaratırken sessizce faturanı şişiren işlemdir.
DynamoDB Scan'im neden yavaş ve pahalı?
Bir Scan, FilterExpression çalışmadan önce tablodaki her öğeyi okur; bu nedenle kaç satır döndüğünden bağımsız olarak tüm tabloyu okuma maliyetini ödersiniz ve tablo büyüdükçe daha da yavaşlar. Çözüm neredeyse her zaman anahtarlanmış bir Query'dir — erişim modelini bir anahtar etrafında tasarlayın, böylece DynamoDB her şey yerine tek bir partition'a dokunur.
- Bir
Scantüm tabloyu okur, her seferinde. Ne ödediğine ve ne kadar sürdüğüne sonuç sayın değil, boyut karar verir. FilterExpressionmaliyet hakkında bir yalandır. Okuma ölçüldükten sonra çalışır, bu yüzden 12 öğe döndürmek 12 milyon okuma için faturalandırabilir.- Bir
Scanbüyüdükçe yavaşlar. Anahtarlanmış birQuerydüz kalır — tablo ne kadar büyürse büyüsün tek bir partition'a dokunur. - Düzeltme neredeyse her zaman ayar değil, modellemedir. Rutin bir soruya yanıt
vermek için
Scanyapıyorsan, bir anahtarı kaçırıyorsun.
Bir Scan gerçekte ne yapar
SQL'den gelince, SELECT * FROM events WHERE type = 'checkout' bedava gibi gelir —
motorun bir index'i vardır ya da yoktur, ama her iki şekilde de satırları geri alırsın.
DynamoDB'de bunu senin için karara bağlayan bir sorgu planlayıcı yoktur.
Bir Scan, tüm tabloyu sırayla, bir seferde 1 MB dolaşır ve her sayfayı
FilterExpression'ına teslim eder. Filtrenin reddettiği her şey yine de okunur, yine
de ölçülür ve yine de faturandadır. (AWS: Scanning tables)
İşte tuzak budur. Filtre bir WHERE cümlesine benzer, ama sonuç kümesini değiştirir,
asla maliyeti değil. Bir Scan, bir filtre mevcut olsun ya da olmasın aynı okuma
kapasitesini tüketir. (AWS: Scanning tables)
Okuma birimlerini say
DynamoDB okumaları okuma kapasite birimleri (RCU) ile ölçer. Bir RCU, 4 KB'ye kadar bir öğenin tek bir güçlü tutarlı okumasını satın alır; nihai tutarlı okumalar bunun yarısına mal olur. Daha büyük öğeler bir sonraki 4 KB'ye yuvarlanır. (AWS: Read/write capacity mode)
Bir analitik tablosu al, ProductEvents. Her satır izlenen bir etkinliktir:
PK = "TENANT#acme"
SK = "TS#2026-06-23T14:08:55Z#evt_9f3a"
attrs: eventType, sessionId, userId, payloadBytesDiyelim ki 2.000.000 etkinlik tutuyor, her biri ~1 KB, hepsi tek bir yoğun tenant altında. Bugünün checkout'larını istiyorsun. Refleksif hamle:
Scan ProductEvents
FilterExpression: eventType = "checkout"
O filtre belki 40 satır döndürür. Ama Scan önce 2.000.000 öğenin hepsini okudu. Her
biri ~1 KB'de (4 KB başına 1 RCU, nihai tutarlı ≈ 4 KB başına 0,5 RCU), 40 öğe geri
vermek için kabaca 250.000 RCU ölçtün — ve ~500 MB veride sayfaladın.
Şimdi erişim desenini bir anahtar olarak modelle ve bunun yerine onu Query et:
Query ProductEvents
PK = "TENANT#acme"
AND SK begins_with "TS#2026-06-23"
Bu, tek bir partition'ın yalnızca eşleşen dilimini okur. O 40 checkout satırı artı günün diğer etkinlikleri ~2 MB'ye gelirse, 500 MB değil ~2 MB okuma için ödersin. Aynı cevap, maliyetin küçük bir kesri — ve tablo büyüdükçe gecikme düz kalır.
Scan ve Query, ölçülmüş
| Scan + filtre | Anahtarlanmış Query | |
|---|---|---|
| Okumalar | Tablodaki her öğe | Bir partition, SK ile daraltılmış |
| Faturalanan kapasite | Filtreden önce tüm tablo | Yalnızca diliminizdeki öğeler |
| Örneğimiz | ~250.000 RCU (~500 MB) | birkaç yüz RCU (~2 MB) |
| Gecikme | Tablo boyutuyla büyür | Tablo büyüdükçe düz |
| Sonuç sayısı | Maliyet hakkında hiçbir şeye karar vermez | Ne ödediğinle eşleşir |
Tablonun kodladığı ders: bir Scan'de, sonuç sayın ve faturan ilgisizdir. Bir
Query'de, birbirlerini takip ederler.
Scan'den önce karar ver
Çoğu kazara Scan, tek bir sorudan gelir: ihtiyacım olan partition'ı adlandırabilir
miyim? Evetse, bir Query'dir. Hayırsa, düzeltme daha büyük bir filtre değil, bir
anahtardır.
İşte karar akış formunda.
Yol neredeyse her zaman Query'de biter; yalnızca hiçbir anahtar — mevcut ya da
eklenebilir — erişim desenine uymadığında Scan'e düşersin.
Desen gerçek ve yinelenen ama temel tablo onu anahtarlayamıyorsa, bu, sorunun bir
Query haline gelmesi için bir Global Secondary Index eklemenin
sinyalidir. Anahtarlarını erişim desenlerinin etrafında en baştan modellemek tüm
oyundur — bkz. single-table tasarım.
Bir filtre değil, anahtarlanmış sorguyu yaz
Anahtarın ötesinde bir koşula gerçekten ihtiyacın olduğunda, her şeyi bir
FilterExpression'a boşaltmak yerine onu kasıtlı olarak oluştur.
DynamoDB Expression Builder, senin için
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)
Bir Scan gerçekten uygun olduğunda
Bir Scan yasak değildir — sadece yanlış varsayılandır. Gerçekten "her şeyi oku"
demek istediğinde doğru araçtır:
- Tek seferlik dışa aktarımlar veya elle çalıştırılan geri-doldurmalar (backfill).
- Küçük yapılandırma / arama tabloları, tüm tablonun birkaç KB olduğu yerde.
- Tüm tabloyu kasıtlı olarak sayfalayan arka plan işleri. Bunları, tek bir uzun
sıralı tarama yerine
Segment/TotalSegmentsile işçiler arasında böl — bir paralel tarama. (AWS: Scanning tables)
Ve PartiQL'in seni kurtarmadığına dikkat et: anahtar yüklemi olmayan
SELECT * FROM ProductEvents WHERE eventType = 'checkout', doğrudan bir Scan'e
derlenir. SQL kılığındaki aynı ayak tuzağıdır. (Tam dökümü için bkz.
Query ve Scan.)
Gerçekten öğeler arası analitiğe ihtiyacın olduğunda — bir GROUP BY, bir JOIN,
DynamoDB'nin ifade edemeyeceği bir toplama (aggregate) — DynoTable'ın SQL Workbench'i
bunları, tabloyu tam bir Scan ile dövmek yerine sınırlı bir sonuç kümesi üzerinde
istemci tarafında çalıştırır.
Sonraki adımlar
Her iki desenin de ne kadara mal olacağını kapasite hesaplayıcısı ile tahmin et, API düzeyindeki karşıtlık için Query ve Scan'i oku ve bunları kendi tablolarına karşı çalıştırmak ve tüketilen kapasiteyi kendin görmek için DynoTable'ı indir.