Başlangıç6 dakikalık okuma

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.

  • Scan her seferinde tüm tabloyu okur. Ödediğin ve ne kadar sürdüğü sonuç sayına değil boyuta bağlıdır.
  • FilterExpression maliyet konusunda yalan söyler. Okuma ölçüldükten sonra çalışır; 12 öğe döndürmek 12 milyon okuma faturalandırabilir.
  • Scan büyüdükçe yavaşlar. Anahtarlı bir Query dü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 Scan ile 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, payloadBytes

Diyelim 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 + filtreAnahtarlı Query
OkumalarTablodaki her öğeBir partition, SK ile daraltılmış
FaturalananFiltre öncesi tüm tabloYalnızca dilimindeki öğeler
Örneğimiz~250.000 RCU (~2 GB)birkaç yüz RCU (~2 MB)
GecikmeTablo boyutuyla büyürTablo 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:

EvetHayırEvetHayırÖğeleri okumak gerekiyorPartition key biliniyor mu?Query tek partitionBir GSI bunu anahtarlayabilir mi?GSI ekle, sonra QueryScan son çare

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 / TotalSegments ile 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.

Güncellendi