Eksiksiz DynamoDB Rehberi — Kavramlar, Desenler ve En İyi Uygulamalar

5 Ağustos 2026 · netologist · 23 dakika, 4753 kelime ·

Amazon DynamoDB üzerinde production sistemler tasarlayan, geliştiren ve işleten mühendisler için derinlemesine bir referans kaynağı.


İçindekiler

  1. Giriş ve Zihinsel Model
  2. Temel Kavramlar
  3. Birincil Anahtarlar: Partition Key ve Sort Key
  4. Veri Tipleri
  5. Kapasite Modları: On-Demand vs Provisioned
  6. İkincil İndeksler: GSI ve LSI
  7. Veri Modelleme Felsefesi
  8. Single-Table (Tek Tablo) Tasarımı
  9. Temel Tasarım Desenleri
  10. Tutarlılık (Consistency) Modelleri
  11. Transactionlar
  12. Batch (Toplu) İşlemler
  13. DynamoDB Streams
  14. Time To Live (TTL)
  15. DAX — DynamoDB Accelerator
  16. Global Tables (Çok Bölgeli Kullanım)
  17. Güvenlik En İyi Uygulamaları
  18. İzleme ve Gözlemlenebilirlik
  19. Maliyet Optimizasyonu
  20. Hata Yönetimi ve Retry Stratejileri
  21. SDK Örnekleri (Node.js & Python)
  22. Yaygın Anti-Pattern’ler
  23. Yedekleme, Geri Yükleme ve Migrasyon
  24. En İyi Uygulamalar Kontrol Listesi

1. Giriş ve Zihinsel Model

DynamoDB, AWS tarafından geliştirilen, tamamen yönetilen (fully managed), sunucusuz (serverless), key-value ve doküman tabanlı bir NoSQL veritabanıdır. Her ölçekte tek haneli milisaniye seviyesinde performans sunmak üzere tasarlanmıştır. İlişkisel veritabanlarının aksine DynamoDB; join, foreign key veya ölçekte keyfi attribute’lar üzerinde ad-hoc sorgular sunmaz. Bunun yerine sorgu esnekliğini, öngörülebilir ve yatay olarak ölçeklenebilir performans ile takas eder.

SQL’den DynamoDB’ye geçerken yapılması gereken en önemli zihinsel değişim şudur:

SQL’de önce veriyi modellersiniz, sorguları sonra bulursunuz. DynamoDB’de ise önce erişim desenlerinizi (access pattern) bilmeniz gerekir, veriyi bunların etrafında modellersiniz.

Bir DynamoDB tablosunu normalize edilmiş bir ilişkisel şema gibi tasarlarsanız bir duvara çarparsınız — ya uygulama tarafında join yapmak zorunda kalırsınız (yavaş, pahalı) ya da pahalı Scan operasyonlarına mecbur kalırsınız.

DynamoDB Ne Zaman Uygundur?

DynamoDB Ne Zaman Uygun Değildir?


2. Temel Kavramlar

Kavramİlişkisel KarşılığıAçıklama
Table (Tablo)TableItem’ların bir koleksiyonu
ItemRow (Satır)Tek bir kayıt, maksimum 400 KB
AttributeColumn (Sütun)Bir item üzerindeki key-value alanı (şemasız — aynı tablodaki item’lar farklı attribute’lara sahip olabilir)
Primary KeyPrimary KeyHer item’ı benzersiz olarak tanımlar; basit (sadece PK) ya da kompozit (PK + SK) olabilir

Önemli gerçekler:


3. Birincil Anahtarlar: Partition Key ve Sort Key

3.1 Basit Primary Key (Sadece Partition Key)

PK: UserId

Her item’ın benzersiz bir UserId‘si olmalıdır. Saf key-value aramalar için uygundur.

3.2 Kompozit Primary Key (Partition Key + Sort Key)

PK: UserId       SK: OrderId

Bu, DynamoDB modellemesinin bel kemiğidir. Aynı partition key’i paylaşan tüm item’lar birlikte, sort key’e göre sıralanmış şekilde depolanır — bu sayede tek bir istekle ilişkili item’ların bir aralığını Query ile çekebilirsiniz (örn. “X kullanıcısının tarihe göre sıralanmış tüm siparişleri”).

3.3 Partitioning Gerçekte Nasıl Çalışır?

DynamoDB, item’ın hangi fiziksel partition’da saklanacağını belirlemek için partition key değerini hash’ler. Bunun anlamı:

3.4 İyi Bir Partition Key Seçmek

3.5 Sort Key Tasarım Teknikleri

Sort key sadece bir ID değildir — güçlü bir modelleme aracıdır:


4. Veri Tipleri

Skaler Tipler

Doküman Tipleri

Set Tipleri

Pratik notlar


5. Kapasite Modları: On-Demand vs Provisioned

On-Demand

Provisioned (Auto Scaling ile)

Kapasite Birimi Matematiği

Genel Kural

Yeni/bilinmeyen iş yükleri için On-Demand ile başlayın. Trafik desenleri stabilize olduğunda ve temel yükü tahmin edebildiğinizde, maliyeti düşürmek için Provisioned + Auto Scaling’e geçin.


6. İkincil İndeksler: GSI ve LSI

6.1 Global Secondary Index (GSI)

6.2 Local Secondary Index (LSI)

6.3 GSI vs LSI — Hangisi Ne Zaman Kullanılır?

FaktörGSILSI
Farklı partition key gerekli mi?EvetHayır — ana tablo ile aynı PK
Tablo oluşturulduktan sonra eklenebilir mi?EvetHayır
Güçlü tutarlılık gerekli mi?Hayır (sadece nihai tutarlı)Evet
Ayrı kapasite/maliyetEvetHayır (ana tablo kapasitesini paylaşır)
Partition başına 10 GB limitiHayırEvet

Pratik tavsiye: Varsayılan olarak GSI kullanın. LSI’ları yalnızca aynı entity grubu için alternatif bir sıralama düzeninde güçlü tutarlılığa ihtiyacınız olduğunda ve partition başına item koleksiyonunun 10 GB’ın çok altında kalacağından eminseniz kullanın.

6.4 Sparse (Seyrek) İndeksler

Bir indeks, yalnızca indekslenen attribute’a sahip item’ları içerir. Bu, bilinçli ve güçlü bir desendir: eğer bir GSI1PK attribute’unu yalnızca belirli bir sorguda görünmesi gereken item’lara eklerseniz (örn. sadece “aktif” siparişler), indeks küçük ve ucuz kalır ve bedavaya örtük bir filtre elde edersiniz.

Örnek: StatusGSI_PK = "PENDING" değerini yalnızca bekleyen siparişlere ekleyin. Tamamlanmış siparişler bu GSI’da hiç görünmez — filtreleme gerekmez, ilgisiz item’ları taramak için harcanan RCU olmaz.

6.5 İndeks Overloading (Genel Amaçlı İndeksler)

Single-table tasarımda, GSI’ları genellikle genel isimlerle adlandırırsınız — GSI1PK, GSI1SK, GSI2PK, GSI2SK — ve aynı fiziksel indeksi farklı entity tipleri için farklı ilişkileri temsil etmek üzere yeniden kullanırsınız. Bu, birkaç indeksin onlarca erişim desenini desteklemesini sağlar.

6.6 Projections (Projeksiyonlar)

Her indeks, ana tablodan bir attribute alt kümesini projekte eder:

En iyi uygulama: yalnızca sorgunun ihtiyaç duyduğunu projekte edin. Aşırı projeksiyon, depolama ve yazma kapasitesini boşa harcar; yetersiz projeksiyon ise ana tabloya pahalı ek GetItem çağrılarına zorlar.


7. Veri Modelleme Felsefesi

5 Adımlı Modelleme Süreci

  1. Domaininizdeki her entity’yi listeleyin (Users, Orders, Products, Reviews…).
  2. Uygulamanızın ihtiyaç duyduğu her erişim desenini listeleyin — admin/raporlama sorguları dahil, tüketici olun. Bunları cümleler halinde yazın: “ID ile kullanıcı getir”, “Son 30 gündeki bir kullanıcının tüm siparişlerini getir”, “Bu ay satışa göre en iyi 10 ürünü getir”.
  3. Entity’ler arasındaki ilişkileri belirleyin (1:1, 1:N, N:M).
  4. Primary key ve indeks yapınızı, her erişim deseninin tek bir Query‘ye (ya da bazen GetItem‘a) eşlenecek şekilde tasarlayın — kritik production sorguları için asla Scan kullanılmamalıdır.
  5. Doğrulayın: tasarımınızı her erişim deseniyle karşılaştırarak yürüyün (ER-diyagramına benzer bir egzersiz: “Entity Relationship Diagram → Access Pattern Table”).

Erişim Desenine Dayalı Tablo Örneği

Erişim DeseniİndeksKey Condition
ID ile kullanıcı profili getirAna tabloPK = USER#123, SK = PROFILE
Bir kullanıcının tüm siparişlerini getirAna tabloPK = USER#123, SK begins_with ORDER#
Sipariş ID’siyle siparişi getirGSI1GSI1PK = ORDER#456
PENDING durumundaki tüm siparişleri getirGSI2 (sparse)GSI2PK = STATUS#PENDING
Bir ürünün tüm yorumlarını getirAna tablo (ürün merkezliyse) veya GSI3PK = PRODUCT#789, SK begins_with REVIEW#

NoSQL Modelleme Prensipleri


8. Single-Table (Tek Tablo) Tasarımı

Nedir?

Her entity tipi için ayrı bir DynamoDB tablosu (ilişkisel tablolar gibi) yerine, birden fazla entity tipini tek bir fiziksel tabloda, genel anahtar isimleri (PK, SK) ve tip-prefixli değerler kullanarak entity’leri ayırt edecek şekilde saklarsınız.

PK              SK                  Attributes
USER#123        PROFILE             name, email, createdAt
USER#123        ORDER#2024-01-01    total, status
USER#123        ORDER#2024-02-15    total, status
ORDER#456       METADATA            userId, total, items[]
PRODUCT#789     PROFILE             name, price, stock
PRODUCT#789     REVIEW#001          rating, text, userId

Neden Single-Table Tasarım?

Neden Tartışmalıdır?

Pratik Tavsiye

Single-table tasarımı, sonlu ve iyi anlaşılmış bir erişim deseni kümesine sahip olduğunuzda ve ölçek/maliyet gerçekten önemli olduğunda kullanın. Gereksinimler hâlâ evrilmekteyse, ekip DynamoDB’ye yeniyse veya sorgu desenleri gerçekten heterojense (örn. farklı ihtiyaçları olan birçok farklı downstream tüketiciye hizmet veren bir platform) çok-tablolu tasarımı (her entity için bir tablo, ya da bounded context başına birkaç tablo) kullanın.


9. Temel Tasarım Desenleri

9.1 Adjacency List (Bitişiklik Listesi) Deseni — Çoktan-Çoğa Modelleme

İlişkileri item’ların kendisi olarak modelleyin. Örn. Kullanıcılar birçok Gruba ait, Grupların birçok Kullanıcısı var:

PK          SK              Type
USER#1      GROUP#A         Membership
USER#1      GROUP#B         Membership
GROUP#A     USER#1          Membership   (GSI üzerinden yansıtılır, PK/SK yer değiştirir)
GROUP#A     USER#2          Membership

GSI1PK = SK, GSI1SK = PK şeklinde bir GSI, aynı item kümesinden her iki yönde de sorgulama yapmanızı sağlar (“bir kullanıcının grupları” ve “bir grubun kullanıcıları”).

9.2 Kompozit/Hiyerarşik Sort Key’ler

SK = "ORG#acme#DEPT#eng#TEAM#platform#USER#42"

begins_with(SK, "ORG#acme#DEPT#eng"), o departmandaki herkesi, herhangi bir takımda, tek bir sorguda getirir.

9.3 Write Sharding (Sıcak Partition’lar İçin)

Bir partition key’in doğal olarak orantısız trafik alacağı durumlarda (örn. global bir liderlik tablosu, popüler bir “trend” sayacı, çok büyük hacme sahip tek bir tenant), rastgele veya hesaplanmış bir shard soneki ekleyin:

PK = "COUNTER#pageviews#shard-7"   (shard = hash(bir_şey) % N)

Okumaların ardından tüm N shard’a yayılıp toplanması gerekir — yazma tarafında partition throttling’i önlemek için buna değer bir takas.

9.4 Zaman Serisi (Time-Series) Veri Deseni

Sınırsız partition büyümesini ve sıcak “bugün” partition’larını önlemek için zaman dilimine göre dönen (rolling) bir partition key kullanın:

PK = "METRIC#cpu#2024-06"     SK = "2024-06-15T10:00:00Z"

Belirli bir ayın verisini tek bir Query‘de sorgulayın; eski segmentler daha ucuz depolamaya taşınabilir (Streams + Firehose ile S3’e) veya TTL ile süresi doldurulabilir.

9.5 Filtreleme İçin Sparse İndeks

§6.4’te ele alındı — yalnızca bir koşulu sağlayan item’lar GSI attribute’una sahip olur, bu da pahalı bir Scan+Filter‘ı ucuz bir Query‘ye dönüştürür.

9.6 Materialized Agregasyonlar / Sayaçlar

Okuma zamanında agregat hesaplamak yerine, atomik sayaçlarla çalışan bir toplam sürdürün:

UpdateItem({
  Key: { PK: "PRODUCT#789", SK: "METADATA" },
  UpdateExpression: "ADD reviewCount :inc, ratingSum :rating",
  ExpressionAttributeValues: { ":inc": 1, ":rating": 5 }
})

9.7 Versiyonlama / Optimistic Locking (İyimser Kilitleme)

Kaybolan güncellemeleri önlemek için koşullu bir UpdateExpression ile birlikte bir version attribute’u kullanın:

UpdateItem({
  ConditionExpression: "version = :expectedVersion",
  UpdateExpression: "SET #data = :newData, version = version + :one",
})

9.8 Sparse GSI’lar Aracılığıyla Soft Delete ve Durum Bayrakları

status != 'deleted' için fiziksel olarak silmek veya taramak yerine, yalnızca mevcut (silinmemiş) item’lar bir GSI_ActivePK attribute’u taşır — silinen item’lar o indeksten basitçe düşer.

9.9 Connection/Cursor Sayfalama (Pagination)

DynamoDB Query/Scan, sayfalama için bir LastEvaluatedKey döndürür — bunu bir sonraki çağrıda ExclusiveStartKey olarak geri iletin. Bir istemciye opak bir cursor olarak sunuyorsanız bunu kodlayın (örn. base64 JSON); ham anahtar yapısını asla güvenilmeyen istemcilere açmayın.

9.10 Filtreleme vs Key Condition’lar


10. Tutarlılık (Consistency) Modelleri

Tavsiye: Özel bir doğruluk gereksiniminiz olmadıkça (örn. kritik bir güncellemeden hemen sonra “kendi yazdığınızı okuma”, finansal bakiyeler, checkout anındaki envanter sayıları) varsayılan olarak nihai tutarlı okumaları kullanın.


11. Transactionlar

TransactWriteItems ve TransactGetItems, potansiyel olarak birden fazla tabloya (aynı hesap/bölge) yayılan birden fazla item üzerinde ACID transaction’ları sağlar.

Yetenekler

Örnek: İki Hesap Arasında Para Transferi

TransactWriteItems({
  TransactItems: [
    {
      Update: {
        Key: { PK: "ACCT#1" },
        ConditionExpression: "balance >= :amount",
        UpdateExpression: "SET balance = balance - :amount",
        ExpressionAttributeValues: { ":amount": 100 }
      }
    },
    {
      Update: {
        Key: { PK: "ACCT#2" },
        UpdateExpression: "SET balance = balance + :amount",
        ExpressionAttributeValues: { ":amount": 100 }
      }
    }
  ]
})

Ne Zaman Kullanılır / Kaçınılır


12. Batch (Toplu) İşlemler

BatchGetItem

BatchWriteItem

En iyi uygulama: UnprocessedKeys/UnprocessedItems için her zaman retry mantığı uygulayın — DynamoDB, kısmi batch başarısızlıklarını sizin için otomatik olarak yeniden denemez.


13. DynamoDB Streams

Streams, item seviyesindeki değişikliklerin (ekleme, güncelleme, silme) zaman sıralı bir dizisini yakalar ve 24 saat boyunca saklar, şu yollarla kullanılabilir:

Görünüm (View) Tipleri

Yaygın Kullanım Alanları

Dikkat Edilmesi Gerekenler


14. Time To Live (TTL)

Yaygın Kullanımlar


15. DAX — DynamoDB Accelerator

DAX, özellikle DynamoDB için tamamen yönetilen, bellek içi (in-memory) bir cache’dir ve mikrosaniye seviyesinde okuma gecikmesi sağlar.


16. Global Tables (Çok Bölgeli Kullanım)

Global Tables şunları sağlayan çok bölgeli, çoklu-aktif (multi-active) replikasyon sunar:

Ne Zaman Kullanılır?


17. Güvenlik En İyi Uygulamaları


18. İzleme ve Gözlemlenebilirlik

Önemli CloudWatch Metrikleri

Contributor Insights

Manuel log inceleme yapmadan sıcak partition sorunlarını teşhis etmek için paha biçilmez olan, en sık erişilen ve en çok throttle edilen anahtarları tespit eder.

CloudTrail

Denetim amacıyla tüm kontrol düzlemi (control-plane) API çağrılarını (tablo oluşturma, indeks değişiklikleri, IAM ile ilgili etkinlik) loglar.

Pratik Alarm Kurulumu


19. Maliyet Optimizasyonu


20. Hata Yönetimi ve Retry Stratejileri

Yaygın Hatalar

Retry Stratejisi


21. SDK Örnekleri (Node.js & Python)

Node.js (AWS SDK v3, DocumentClient)

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import {
  DynamoDBDocumentClient,
  PutCommand,
  GetCommand,
  QueryCommand,
  UpdateCommand,
} from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);

// Item ekleme
await docClient.send(new PutCommand({
  TableName: "AppTable",
  Item: { PK: "USER#123", SK: "PROFILE", name: "Ada", email: "[email protected]" },
  ConditionExpression: "attribute_not_exists(PK)",
}));

// Item getirme
const { Item } = await docClient.send(new GetCommand({
  TableName: "AppTable",
  Key: { PK: "USER#123", SK: "PROFILE" },
}));

// İlişkili item'ların bir aralığını sorgulama
const { Items } = await docClient.send(new QueryCommand({
  TableName: "AppTable",
  KeyConditionExpression: "PK = :pk AND begins_with(SK, :prefix)",
  ExpressionAttributeValues: { ":pk": "USER#123", ":prefix": "ORDER#" },
}));

// Atomik sayaç güncelleme
await docClient.send(new UpdateCommand({
  TableName: "AppTable",
  Key: { PK: "PRODUCT#789", SK: "METADATA" },
  UpdateExpression: "ADD viewCount :inc",
  ExpressionAttributeValues: { ":inc": 1 },
}));

Python (boto3)

import boto3
from boto3.dynamodb.conditions import Key, Attr

dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("AppTable")

# Item ekleme
table.put_item(
    Item={"PK": "USER#123", "SK": "PROFILE", "name": "Ada", "email": "[email protected]"},
    ConditionExpression=Attr("PK").not_exists(),
)

# Item getirme
response = table.get_item(Key={"PK": "USER#123", "SK": "PROFILE"})
item = response.get("Item")

# İlişkili item'ların bir aralığını sorgulama
response = table.query(
    KeyConditionExpression=Key("PK").eq("USER#123") & Key("SK").begins_with("ORDER#")
)
items = response["Items"]

# Atomik sayaç güncelleme
table.update_item(
    Key={"PK": "PRODUCT#789", "SK": "METADATA"},
    UpdateExpression="ADD viewCount :inc",
    ExpressionAttributeValues={":inc": 1},
)

22. Yaygın Anti-Pattern’ler

Anti-PatternNeden Sorun?Çözüm
Kritik istek yolunda Scan kullanmakTüm tabloyu okur, muazzam RCU tüketir, ölçekte yavaştırBir Query‘nin deseni kapsayacağı şekilde anahtarları/indeksleri yeniden tasarlayın
İlişkisel bir veritabanı gibi modelleme (birçok küçük normalize edilmiş tablo + uygulama tarafında join)Birçok round-trip, yüksek gecikme, yüksek maliyet gerektirirDenormalize edin; single-table veya erişim-desenine-özel tablolar kullanın
Tek PK olarak düşük kardinaliteli partition key’ler (örn. Status, Country)Sıcak partition’lar ve throttling yaratırYüksek kardinaliteli bir bileşen ekleyin veya anahtarı shard’layın
Büyük blob’ları (>400 KB) doğrudan saklamakItem boyut limitini aşarS3’te saklayın, DynamoDB’de bir pointer/referans tutun
Performans için FilterExpression‘a güvenmekFiltreleme okumadan sonra gerçekleşir — taranan item’lar için yine de tam RCU ödersinizFiltrelemenin KeyConditionExpression üzerinden gerçekleşmesi için anahtar şemasını yeniden tasarlayın
Transaction’ları aşırı kullanmak2 kat kapasite maliyeti, eklenen gecikme, eklenen karmaşıklıkYalnızca gerçek çok-item değişmezleri için kullanın
Batch çağrılarda UnprocessedItems/UnprocessedKeys‘i görmezden gelmekKısmi batch başarısızlıklarında sessiz veri kaybıHer zaman kontrol edin ve yeniden deneyin
Modellemeden önce erişim desenlerini planlamamakİleride pahalı yeniden tasarımlara veya Scan-ağırlıklı geçici çözümlere zorlarBaştan 5 adımlı modelleme sürecini uygulayın (§7)
Bir LSI’da sınırsız item koleksiyonlarıPartition-key başına 10 GB sert limitine takılırBunun yerine bir GSI kullanın veya partition key’i shard’layın

23. Yedekleme, Geri Yükleme ve Migrasyon


24. En İyi Uygulamalar Kontrol Listesi


İleri Okuma