Orta7 dakikalık okuma

DynamoDB Filtreleme Stratejileri

DynamoDB'de "filtreleme", aynı sözcüğü giyen dört farklı şey demektir. Üçü, veriyi okunmadan ve faturalandırılmadan önce daraltır; biri — Filter adlı olan — onu sonradan daraltır. Hangisinin hangisi olduğunu bilmek, becerinin çoğudur.

DynamoDB'de filtreleme nasıl çalışır?

DynamoDB'nin filtrelemenin dört yolu vardır ve yalnızca biri faturalandırıldıktan sonra çalışır. bir partition seçer, sort key bir dilim daraltır ve bir sparse index öznitelik varlığına göre filtreler — üçü de okuma maliyetini ölçmeden önce kısar. Bir FilterExpression ise okumadan sonra çalışır, dolayısıyla yanıtı küçültür ama faturayı asla küçültmez.

  • en ucuz filtredir: partition'ı seçer, böylece tablonun geri kalanına asla dokunmazsın.
  • , bir partition içinde begins_with, between, <, > ile filtreler — yine faturalandırmadan önce, yine ucuz.
  • , yoklukla filtreler: bir öğe, yalnızca indekslenen özniteliğe sahipse index'te görünür, dolayısıyla index, filtrelenmiş kümenin kendisidir.
  • FilterExpression tuzaktır: DynamoDB okumayı ölçtükten sonra çalışır, dolayısıyla yanıt boyutunu kısar ama faturanı asla.

Örneği kur

Bir ürün kataloğu. Tek bir tablo, partition key PK, sort key SK:

PK = "DEPT#kitchen"   SK = "PROD#00194"

Her ürün ayrıca price, inStock (bir boolean) ve clearanceAt (bir unix zaman damgası, yalnızca tasfiye için işaretlenmiş öğelerde bulunur) taşır. Bir departmandaki öğeler bir partition'ı paylaşır, ürün kimliğine göre sıralanır.

Dört erişim deseni istiyoruz. Her biri farklı bir filtreleme stratejisine eşlenir — ve herhangi birinde yanlış seçim, sonsuza dek ödeyeceğin bir Scan'dir.

Partition key'e göre filtrele

"kitchen'daki her ürünü ver." Partition key bunu doğrudan yanıtlar:

Query  PK = "DEPT#kitchen"

DynamoDB tam olarak bir partition okur. Tablodaki başka hiçbir şeye dokunulmaz veya faturalandırılmaz. Bu, önemli anlamda bedava olan tek filtredir — Query ile Scan arasındaki fark.

SQL'den geliyorsan bu tersmiş gibi gelir: bir index'i tarayan bir WHERE department = 'kitchen' yoktur, sen sadece partition'ı adlandırırsın. Onu adlandıramıyorsan, bu bir sorgu sorunu değil, bir modelleme sorunudur.

Sort key'e göre filtrele

"Bana PROD#00100'den yukarı kitchen ürünlerini ver." Sort key, partition içini daraltır ve bunu okuma ölçülmeden önce yapar:

Query  PK = "DEPT#kitchen"  AND  SK between "PROD#00100" AND "PROD#00200"

Sort key koşulları bilinçli olarak sınırlıdır: =, <, <=, >, >=, between ve begins_with. OR yok, rastgele yüklem yok.

O kısıtlama, okumayı hedefli tutan şeydir — DynamoDB tüm partition'ı değil, bitişik bir dilimi yürür.

Buradaki kaldıraç, sort key'i nasıl kodladığındır. Desenin "fiyat aralığına göre" ise, bir PROD#<id> sort key yardımcı olmaz — fiyatı anahtara gömerdin.

Bu, sorgu zamanında değil, tasarım zamanında verilen bir sort-key stratejisi kararıdır.

Sparse index'e göre filtrele

"Şu anda tasfiyede olan her şeyi ver." Çoğu ürün değil, dolayısıyla az sayıda olanı bulmak için kataloğu okumak istemezsin.

Bir sparse index bunu yoklukla çözer. Bir , yalnızca bir öğe index'in anahtar özniteliklerinin ikisine de sahipse o öğeyi içerir.

GSI partition key'i olarak sabit bir clearance = "CLEARANCE" bayrağı ayarla — yalnızca tasfiye öğelerine yazılmış — sort key olarak clearanceAt ile ve index başka hiçbir şey tutmaz.

AWS bunu açıkça belirtir: bir global secondary index yalnızca index'in anahtar özniteliklerine sahip öğeleri içerir, dolayısıyla anahtar özniteliğinden yoksun öğeler basitçe yayılmaz (AWS — Sparse index'lerden yararlanın).

EvetHayırTemel tablo tüm ürünlerclearanceAt var mı?ClearanceIndex'e yayılırIndex'te yokIndex'i sorgula = yalnızca tasfiyeöğeleri

Şimdi sorgu yalnızca tasfiye öğelerini okur, yalnızca onlar için faturalandırılır:

Query  ON ClearanceIndex   GSI_PK = "CLEARANCE"   (sorted by clearanceAt)

Filtre, veriyi yazdığında gerçekleşti — clearanceAt'i hiç ayarlayıp ayarlamamayı seçerek. Index, filtrelenmiş kümedir. Hangi index türünün uyduğu için bkz. GSI vs LSI.

FilterExpression ile filtrele

"Bana stokta olan kitchen ürünlerini ver." inStock bir anahtar öznitelik değil, dolayısıyla bir FilterExpression'a uzanırsın:

Query  PK = "DEPT#kitchen"
Filter inStock = true

İşte tuzak. DynamoDB kitchen partition'ındaki her öğeyi okur, hepsi için kapasiteyi ölçer ve sonra stokta olmayanları düşürür.

AWS şunu belirtir: bir filtre ifadesi, "bir Query bittikten sonra ama sonuçlar döndürülmeden önce uygulanır" ve "bir Query, bir filtre ifadesi mevcut olup olmadığına bakılmaksızın aynı miktarda okuma kapasitesi tüketir" — tam okuma için zaten ödedin (AWS — Query için filtre ifadeleri).

Yani kitchen'da 10.000 ürün varsa ve 12'si stoktaysa, 10.000'i okumaya ödersin. Yanıt küçük; fatura değil. FilterExpression, telden geçen yükü küçültür, okumayı asla.

İkinci, daha keskin bir kenar daha var: sayfalama, filtrelemeden önce ölçülür. Bir sayfa, 1 MB okunan öğedir, 1 MB eşleşme değil.

Bir filtre, ayarlanmış bir LastEvaluatedKey ile boş bir sayfa döndürebilir — DynamoDB tam bir megabayt okudu, hiçbir şey eşleşmedi, sana boş bir dizi verdi. Sayfalamaya devam edersin ve her boş sayfa için ödedin.

İfadeyi — adlar, değerler ve doğru ayrılmış-sözcük kaçışı — DynamoDB Expression Builder ile oluştur ki #inStock/:val yer tutucuları ilk seferde doğru olsun.

Aşağıdaki builder, bir FilterExpression içeren bir Scan için önceden ayarlıdır — yukarıdaki tam anti-desen. Filtrenin bir anahtar dilimi değil, tüm tablo üzerinde çalıştığına dikkat et:

İsteğini oluştur
Oluşturulan kod
new ScanCommand({
  "TableName": "AuditLog",
  "FilterExpression": "#filter0 = :filterValue0",
  "ExpressionAttributeNames": {
    "#filter0": "action"
  },
  "ExpressionAttributeValues": {
    ":filterValue0": {
      "S": "delete"
    }
  }
})

Dördünü karşılaştır

Ne zaman filtrelerOkuma maliyetini kısar mı?Yüklem gücüKurma maliyeti
Partition keyOkumadan önceEvet — bir partitionYalnızca eşitlikBedava (anahtarın kendisi)
Sort keyOkumadan önceEvet — bir dilimAralık / begins_withSort key tasarımı
Sparse indexOkumadan önceEvet — yalnızca indexBir özniteliğin varlığıEk GSI + yazma maliyeti
FilterExpressionOkumadan sonraHayırNeredeyse her koşulYok

Tabloyu yukarıdan aşağıya oku: yüklem gücü yükselir, maliyet kontrolü düşer. FilterExpression her şeyi tam olarak ifade edebilir, tam da zaten okunmuş öğeler üzerinde çalıştığı için — sana para kazandıramamasının nedeni de aynı.

DynoTable'da görüntüleyin

Bir filtreyle bir Query çalıştırdığında, okunan öğeler ile döndürülen öğeler arasındaki boşluk bütün hikayedir. DynoTable, filtreli bir okuma akarken taranan öğeleri döndürülen öğelerin yanında gösterir — böylece bir partition'ı sessizce baştan sona okuyan bir filtre, aylık faturanda gizlenmek yerine görünür olur.

Bir filtrenin yanıtlayamayacağı gerçek öğeler-arası sorular için — "departman başına ortalama fiyat", "yorumlarına join edilmiş stoktaki ürünler" — DynoTable'ın SQL Workbench'i, tablo-genişliğinde bir Scan'e derlenmek yerine, sınırlı bir sonuç kümesi üzerinde istemci tarafında GROUP BY, JOIN ve toplamalar çalıştırır.

Tuzaklar ve sonraki adımlar

  • FilterExpression'ı birincil erişim yolun olarak kullanma. Bir desen yaygınsa, onu bir anahtara veya bir sparse index'e modelle. Bir filtre, son ufak daraltma için, işin çoğu için değil.
  • Boş sayfalara dikkat et. Filtreli bir sorgu uzun süre hiçbir şey döndürmeden sayfalayabilir. LastEvaluatedKey'e uy; boş bir sayfanın "bitti" anlamına geldiğini varsayma.
  • Bir sparse index bedava değildir. İçine düşen her öğe için yazma kapasitesi ve depolama tutar — öznitelik nadir olduğunda ucuz, olmadığında daha az.

Filtreli bir okumanın gerçekte ne tutacağını fiyatlandırma hesaplayıcısı ile tahmin et ve kendi tablolarında tüketilen kapasiteyi döndürülen satırlara karşı izlemek için DynoTable'ı dene.

Güncellendi