Orta6 dakikalık okuma

DynamoDB'de Bire-Çok İlişkiler

Bir SaaS denetim düzleminin neredeyse her zaman bir kapsama hiyerarşisi vardır: bir workspace birçok projeye sahiptir. SQL'de projeler tablosuna bir workspace_id yabancı anahtarı koyup JOIN yapardın.

DynamoDB'nin join'leri ve yabancı anahtarları yoktur, bu yüzden ilişkinin anahtar şemasının kendisinde yaşaması gerekir. Doğru yapılırsa, "bir workspace'i ve içindeki her projeyi yükle" işlemi, bir okuma artı bir takip taraması yerine tek bir Query haline gelir.

DynamoDB'de bire-çok ilişkiyi nasıl modellersiniz?

Üst öğeye ve tüm alt öğelerine aynı 'i verin; böylece tek bir paylaşırlar ve onları sort key ile ayırt edin. DynamoDB'nin join'leri ve yabancı anahtarları yoktur, dolayısıyla ilişki anahtar şemasının kendisinde yaşar. Üst öğeyi ve tüm alt öğeleri yüklemek, join yerine tek bir Query haline gelir.

  • Varlıkları değil, okumaları modelle. Bire-çok ilişki yalnızca "bir workspace'in projelerini listele" işlemine hizmet etmek için vardır — anahtarları o sorgunun etrafında şekillendir.
  • Üst öğeyi alt öğenin partition key'ine kodla. Workspace'e ve tüm projelerine aynı partition-key değerini ver, böylece tek bir item collection içinde yer alırlar.
  • O zaman liste okuması tek bir Query'dir. Üst öğe artı keyfi sayıda alt öğe, tek bir faturalandırılan çağrıda geri gelir — join yok, ikinci tur yok.
  • Sıcak partition'a dikkat et. Tek bir devasa tenant tüm trafiğini bir partition'da yoğunlaştırır; çok büyük bir workspace, parçalanmış (sharded) bir anahtar ve bir fan-out okuması gerektirebilir.

Önce erişim deseni

DynamoDB modelleme, varlık öncelikli değil erişim-deseni önceliklidir — single-table tasarımın ardındaki aynı disiplin. Herhangi bir anahtar seçmeden önce, uygulamanın gerçekte yaptığı okumaları yaz:

  • Bir workspace'in ayarlarını al.
  • Bir workspace'teki her projeyi listele, en yenisi ilk.
  • Belirli bir projeyi id ile al.

"Bir workspace, birçok proje" ilişkisi yalnızca 2 numaralı okuma yüzünden önemlidir. Bir workspace'in projelerini birlikte listelemen hiç gerekmeseydi, ilişkiyi hiç modellemezdin — projeleri bağımsız olarak saklardın.

Yani soru hiçbir zaman soyut anlamda "bire-çok ilişkiyi nasıl temsil ederim?" değildir. "Bu ilişkinin hangi sorgulara hizmet etmesi gerekir?" sorusudur. Buna yanıt ver, sonra anahtarları bunun etrafında şekillendir.

Neden burada bir yabancı anahtar işe yaramaz

DynamoDB'de her GetItem ve Query bir partition key hedefler ve servis, öğeyi tutan partition'ı bulmak için o anahtarı hash'ler.

AWS bunu Core Components dokümanlarında doğrudan söyler: partition-key değeri, verinin nerede yaşadığına karar veren dahili bir hash fonksiyonuna girdidir.

Bu hash tabanlı yerleştirme, orijinal 2007 Dynamo: Amazon's Highly Available Key-value Store makalesinden gelen mirastır; burada tutarlı hashing anahtarları düğümlere dağıtır.

Bir proje öğesi üzerindeki çıplak bir workspace_id attribute'u o mekanizmaya görünmezdir — DynamoDB onu "takip edemez".

İlgili öğeleri tek bir istekte getirmek için, üst öğenin kimliği projenin partition key'ine kodlanmalıdır, böylece bir workspace'in tüm öğeleri aynı partition'a hash'lenir ve tek bir Query onları süpürebilir.

İşlenmiş örnek: workspace'ler ve projeler

Genel, aşırı yüklenmiş (overloaded) bir anahtar şeması kullan. Partition key'e EntityRef, sort key'e Detail adını ver. Workspace kimliği, hem workspace öğesi hem de altındaki her proje için EntityRef'e girer:

EntityRefDetailattributes
WS#acmeMETAdisplayName, region, seatLimit
WS#acmePROJ#2026-0007title, status, createdBy
WS#acmePROJ#2026-0042title, status, createdBy
WS#acmePROJ#2026-0118title, status, createdBy
WS#globexMETAdisplayName, region, seatLimit
WS#globexPROJ#2026-0009title, status, createdBy

Workspace ve tüm projeleri EntityRef = "WS#acme" değerini paylaşır, böylece tek bir partition üzerinde birlikte yaşayan tek bir item collection oluştururlar.

Detail sort key onları ayırır: META workspace kaydıdır ve her proje, projelerin doğal olarak sıralanması için sıfırla doldurulmuş, zaman sıralı bir id ile bir PROJ# öneki taşır.

Görsel olarak, üst öğe ve alt öğeleri tek bir partition içinde, sort key'e göre sıralanmış şekilde üst üste yığılır:

Partition: EntityRef = WS#acmeMETA workspace ayarlarıPROJ#2026-0007PROJ#2026-0042PROJ#2026-0118

EntityRef = "WS#acme" üzerinde tek bir Query, tüm yığını — üst öğe artı her alt öğe — tek bir okumada süpürür.

Şimdi üç erişim deseninin her biri tek bir çağrıya indirgenir:

  • Workspace ayarlarıGetItem(EntityRef="WS#acme", Detail="META").
  • Projeleri en yeniden başlayarak listeleQuery(EntityRef="WS#acme") ile Detail begins_with "PROJ#", azalan sırada çalıştırılır (ScanIndexForward = false).
  • Tek bir projeGetItem(EntityRef="WS#acme", Detail="PROJ#2026-0042").

İkincisi tüm mesele: üst öğe ve keyfi sayıda alt öğe, tek bir faturalandırılan Query ile geri gelir, join yok, ikinci tur yok. Bunu bir yabancı-anahtar attribute'u ve bir Scan ile yapamazsın.

O begins_with koşulunu elle yazmak zahmetlidir — key-condition ve projection-expression söz dizimi can yakar.

DynamoDB Expression Builder, KeyConditionExpression'ı, #name/:value placeholder eşlemelerini ve çalıştırmaya hazır bir SDK parçacığını üretir, böylece dilbilgisiyle uğraşmazsın:

KeyConditionExpression     "#er = :er AND begins_with(#d, :p)"
ExpressionAttributeNames   { "#er": "EntityRef", "#d": "Detail" }
ExpressionAttributeValues  { ":er": "WS#acme", ":p": "PROJ#" }

Item collection'ı DynoTable'da incele

Bu düzenin getirisi görseldir: bir EntityRef'i paylaşan her satır, workspace artı alt öğeleridir; yan yana otururlar.

DynoTable onları gruplar, böylece bire-çok ilişkiyi ayrı tablolar arasında tahmin etmek yerine tek bir bitişik blok olarak görürsün.

DynoTable'ın tablo görünümünde tek bir item collection olarak gruplanmış workspace META öğesi ve PROJ# alt öğeleri.
DynoTable'ın tablo görünümünde tek bir item collection olarak gruplanmış workspace META öğesi ve PROJ# alt öğeleri.

Tuzaklar ve alternatif şekil

Dikkat edilecek birkaç şey:

  • Sıcak partition'lar. Bir workspace'in her öğesi tek bir partition'da yaşar, bu yüzden tek bir çok büyük veya çok yoğun tenant trafiği yoğunlaştırır. AWS'nin tanımladığı adaptive capacity davranışı orta düzeydeki çarpıklığı emer, ancak milyonlarca projesi olan bir workspace parçalanmış bir anahtar (örn. WS#acme#01 … #10) ve bir fan-out okuması gerektirebilir.
  • Item-collection boyutu. Bir local secondary index varsa, tek bir partition'ın item collection'ı 10 GB ile sınırlıdır; LSI olmadan böyle bir sınır yoktur. Burada index türlerini tartıyorsan, bkz. GSI ve LSI.
  • Query'ye uzan, asla Scan'e değil. Tüm tasarım, bir partition'ı Query edebilmen için vardır. "Bir workspace'in projelerini bul" diye filtreli bir Scan'e geri dönmek modeli çöpe atar ve tüm tabloyu okur — Query ve Scan'de işlenen tuzak.

Gerçekten workspace'ler arası projeleri listelemen gerekiyorsa (diyelim ki küresel olarak tüm status = ACTIVE projeler), temel tablo buna yanıt veremez — partition key'i workspace kapsamlıdır.

Bu, projeleri farklı bir attribute üzerinden yeniden bölümleyen bir secondary index'in işidir, bu ilişkiyi yeniden şekillendirmenin değil.

Sonraki adımlar

Erişim desenlerini modelle, üst öğeyi alt öğenin partition key'ine kodla ve bire-çok okuması tek bir Query olsun. Anahtar koşulunu DynamoDB Expression Builder ile oluştur ve doğrula.

Sonra bu şemayı yüklemek, workspace→projeler item collection'ını canlı taramak ve her sorgunun tam olarak bir okuma yaptığını doğrulamak için DynoTable'ı indir.

Güncellendi