Eksiksiz DynamoDB Rehberi — Kavramlar, Desenler ve En İyi Uygulamalar
Amazon DynamoDB üzerinde production sistemler tasarlayan, geliştiren ve işleten mühendisler için derinlemesine bir referans kaynağı.
İçindekiler
- Giriş ve Zihinsel Model
- Temel Kavramlar
- Birincil Anahtarlar: Partition Key ve Sort Key
- Veri Tipleri
- Kapasite Modları: On-Demand vs Provisioned
- İkincil İndeksler: GSI ve LSI
- Veri Modelleme Felsefesi
- Single-Table (Tek Tablo) Tasarımı
- Temel Tasarım Desenleri
- Tutarlılık (Consistency) Modelleri
- Transactionlar
- Batch (Toplu) İşlemler
- DynamoDB Streams
- Time To Live (TTL)
- DAX — DynamoDB Accelerator
- Global Tables (Çok Bölgeli Kullanım)
- Güvenlik En İyi Uygulamaları
- İzleme ve Gözlemlenebilirlik
- Maliyet Optimizasyonu
- Hata Yönetimi ve Retry Stratejileri
- SDK Örnekleri (Node.js & Python)
- Yaygın Anti-Pattern’ler
- Yedekleme, Geri Yükleme ve Migrasyon
- 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?
- Yüksek ölçekli, öngörülebilir erişim desenleri (ID ile item getirme, bilinen bir ilişkiye göre item getirme)
- Ölçekten bağımsız tutarlı düşük gecikmeli okuma/yazma ihtiyacı
- Serverless mimariler (Lambda + DynamoDB klasik bir eşleşmedir)
- İyi tanımlanmış, sonlu sorgu desenlerine sahip iş yükleri
- Streams’ten faydalanan olay tabanlı (event-driven) sistemler
DynamoDB Ne Zaman Uygun Değildir?
- Yoğun ad-hoc analitik sorgular, karmaşık join’ler, birçok entity üzerinde agregasyonlar → Redshift, Athena veya bir ilişkisel veritabanı kullanın, ya da veriyi bir data lake’e aktarın
- Sürekli değişen, son derece dinamik/bilinmeyen sorgu desenleri
- Düşük trafikli küçük veri setlerinde Postgres/MySQL’in operasyonel basitliği daha avantajlı olabilir
2. Temel Kavramlar
| Kavram | İlişkisel Karşılığı | Açıklama |
|---|---|---|
| Table (Tablo) | Table | Item’ların bir koleksiyonu |
| Item | Row (Satır) | Tek bir kayıt, maksimum 400 KB |
| Attribute | Column (Sütun) | Bir item üzerindeki key-value alanı (şemasız — aynı tablodaki item’lar farklı attribute’lara sahip olabilir) |
| Primary Key | Primary Key | Her item’ı benzersiz olarak tanımlar; basit (sadece PK) ya da kompozit (PK + SK) olabilir |
Önemli gerçekler:
- DynamoDB tabloları, primary key attribute’ları dışında şemasızdır — diğer tüm attribute’lar isteğe bağlıdır ve item’a göre değişebilir.
- Maksimum item boyutu, attribute adları ve değerleri dahil 400 KB‘dır.
- Bir kez oluşturulan tablo adları yeniden adlandırılamaz.
- DynamoDB varsayılan olarak bölgeye (region) özgüdür (Global Tables bunu genişletir).
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ı:
- Aynı PK‘ya sahip item’lar her zaman aynı partition‘a düşer.
- İyi dağıtılmış bir PK, yükü birçok partition’a yayar → daha yüksek throughput.
- “Sıcak” (hot) bir PK (bir PK değerine çok fazla istek düşmesi), tablo seviyesindeki provisioned kapasite normal görünse bile throttle edilebilir; çünkü her partition’ın kendi throughput tavanı vardır (partition başına 3.000 RCU / 1.000 WCU, adaptive capacity’ye bağlı olarak değişebilir).
3.4 İyi Bir Partition Key Seçmek
- Yüksek kardinalite: çok sayıda farklı değer (örn.
UserId,DeviceId,TenantId) —StatusveyaCountrygibi düşük kardinaliteli anahtarları tek başına PK olarak kullanmaktan kaçının. - Eşit erişim dağılımı: değerlerin %1’inin trafiğin %90’ını aldığı bir PK tasarımından kaçının (örn. tek bir ünlü kullanıcı, tek bir “global sayaç” satırı).
- Baskın erişim deseninizle eşleşmeli: sorguların %90’ı “belirli bir Y için tüm X’leri getir” şeklindeyse,
Ysizin partition key’iniz olmalıdır.
3.5 Sort Key Tasarım Teknikleri
Sort key sadece bir ID değildir — güçlü bir modelleme aracıdır:
- Kompozit/bileşik sort key’ler:
SK = "ORDER#2024-06-01#o-1234"aynı anda hem tarih aralığına hem de prefix’e göre sorgu yapmanızı sağlar. - Hiyerarşik veri:
SK = "COUNTRY#US#STATE#CA#CITY#SF", hiyerarşinin herhangi bir seviyesindebegins_with()sorgularına olanak tanır. - Single-table tasarımda tip-prefix’leme:
SK = "PROFILE",SK = "ORDER#123",SK = "ADDRESS#456"birden fazla entity tipinin tek bir partition key altında yaşamasını sağlar. - Sıralanabilir zaman damgaları: leksikografik sıralama = kronolojik sıralama olması için ISO-8601 (
2024-06-01T12:00:00Z) kullanın. - Sıfır dolgulu sayılar: string sıralamasının sayısal sıralamayla eşleşmesi için
SK = "42"yerineSK = "00042"kullanın (string olarak"9" > "10"ama"09" < "10").
4. Veri Tipleri
Skaler Tipler
String (S),Number (N),Binary (B),Boolean (BOOL),Null (NULL)
Doküman Tipleri
Map (M)— JSON objesi gibidir, nested (iç içe) yapıyı desteklerList (L)— sıralı koleksiyon, karışık tipleri destekler
Set Tipleri
String Set (SS),Number Set (NS),Binary Set (BS)— benzersiz skaler değerlerin sırasız koleksiyonlarıdır. Etiketler, izin listeleri gibi durumlar için kullanışlıdır. Tekrar eden veya boş değer içeremezler.
Pratik notlar
- Sayılar dahili olarak, 38 haneye kadar hassasiyete sahip string’ler olarak saklanır — para için floating point kullanmaktan kaçının; kuruşları integer olarak saklayın veya SDK’nın
Decimal-güvenli tipini kullanın. - 2020’den itibaren boş string ve boş binary değerlere izin verilir, fakat boş Set’lere izin verilmez.
- Alt alanları atomik olarak güncellemeniz gerektiğinde, derin iç içe geçmiş JSON blob’lar yerine
Map/Listtercih edin — DynamoDB, nested attribute’ları doğrudan güncellemeyi destekler (SET item.address.city = :c).
5. Kapasite Modları: On-Demand vs Provisioned
On-Demand
- İstek başına ödeme (tüketilen RCU/WCU başına), kapasite planlaması gerekmez.
- Ani ve öngörülemeyen trafiği anında karşılayacak şekilde ölçeklenir (AWS dokümantasyonuna göre, önceki zirve değerin 30 dakika içinde iki katına kadar — aşırı ani sıçramalar için önceden planlama yapın).
- En uygun olduğu durumlar: yeni uygulamalar, öngörülemeyen/sıçramalı iş yükleri, düşük operasyonel yük önceliği.
- Dezavantajları: iyi kullanılan provisioned kapasiteye göre istek başına yaklaşık %2-2,5 kat daha pahalıdır.
Provisioned (Auto Scaling ile)
- RCU/WCU’yu siz belirlersiniz; Application Auto Scaling, hedef kullanım oranına (örn. %70) göre min/max sınırlar içinde ayarlama yapar.
- En uygun olduğu durumlar: maliyet optimizasyonunun önemli olduğu öngörülebilir, düzenli (steady-state) trafik.
- Sabit temel yük üzerinde ek indirimler için Reserved Capacity satın alımlarını destekler.
Kapasite Birimi Matematiği
- 1 RCU = saniyede 4 KB’a kadar bir güçlü tutarlı (strongly consistent) okuma, YA DA saniyede 4 KB’a kadar iki nihai tutarlı (eventually consistent) okuma.
- 1 WCU = saniyede 1 KB’a kadar bir yazma.
- Transaction’lı okuma/yazmalar normal RCU/WCU’nun 2 katını tüketir.
- Birim boyutundan büyük item’lar yukarı yuvarlanır (örn. 4,5 KB’lık bir item, güçlü tutarlı okuma için 2 RCU tüketir).
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)
- Bir GSI, ana tablodan farklı kendi partition key’ine ve isteğe bağlı sort key’ine sahiptir.
- Kendi provisioned/on-demand kapasitesine sahiptir, ayrıca faturalandırılır.
- Sadece nihai tutarlı (eventually consistent) — GSI’larda güçlü tutarlı okumalar desteklenmez.
- Tablo oluşturulduktan sonra eklenebilir veya kaldırılabilir.
- Tablo başına 20’ye kadar GSI (soft limit, artırılabilir).
- GSI yazmaları asenkrondur — GSI güncellemesi başarısız olsa veya gecikse bile ana tabloya yapılan yazma başarısız olmaz, ancak bu, yoğun yazma patlamaları sırasında GSI’ların kısa süreliğine senkron dışı kalabileceği anlamına gelir (GSI throttling, GSI yetişemezse ana tablonun yazmalarını da throttle edebilir).
6.2 Local Secondary Index (LSI)
- Ana tablonun partition key’ini paylaşır, ancak farklı bir sort key’e sahiptir.
- Sadece tablo oluşturulurken oluşturulabilir — sonradan eklenemez ve tabloyu yeniden oluşturmadan kaldırılamaz.
- Güçlü tutarlı okumaları destekler.
- Ana tablonun throughput kapasitesini paylaşır (ayrı faturalandırma yoktur).
- Ana tablo + tüm LSI’lar birleşiminde, partition key değeri başına 10 GB ile sınırlıdır — bu, birçok ekibi zorlayan sert bir kısıtlamadır.
- Tablo başına en fazla 5 LSI.
6.3 GSI vs LSI — Hangisi Ne Zaman Kullanılır?
| Faktör | GSI | LSI |
|---|---|---|
| Farklı partition key gerekli mi? | Evet | Hayır — ana tablo ile aynı PK |
| Tablo oluşturulduktan sonra eklenebilir mi? | Evet | Hayır |
| Güçlü tutarlılık gerekli mi? | Hayır (sadece nihai tutarlı) | Evet |
| Ayrı kapasite/maliyet | Evet | Hayır (ana tablo kapasitesini paylaşır) |
| Partition başına 10 GB limiti | Hayır | Evet |
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:
KEYS_ONLY— en küçük, en ucuz, yazması en hızlıINCLUDE— anahtarlar + belirtilen attribute listesiALL— tüm attribute’lar (en yüksek depolama maliyeti, ancak ana tabloya ikinci bir gidiş-dönüşü önler)
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
- Domaininizdeki her entity’yi listeleyin (Users, Orders, Products, Reviews…).
- 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”.
- Entity’ler arasındaki ilişkileri belirleyin (1:1, 1:N, N:M).
- Primary key ve indeks yapınızı, her erişim deseninin tek bir
Query‘ye (ya da bazenGetItem‘a) eşlenecek şekilde tasarlayın — kritik production sorguları için aslaScankullanılmamalıdır. - 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 | İndeks | Key Condition |
|---|---|---|
| ID ile kullanıcı profili getir | Ana tablo | PK = USER#123, SK = PROFILE |
| Bir kullanıcının tüm siparişlerini getir | Ana tablo | PK = USER#123, SK begins_with ORDER# |
| Sipariş ID’siyle siparişi getir | GSI1 | GSI1PK = ORDER#456 |
| PENDING durumundaki tüm siparişleri getir | GSI2 (sparse) | GSI2PK = STATUS#PENDING |
| Bir ürünün tüm yorumlarını getir | Ana tablo (ürün merkezliyse) veya GSI3 | PK = PRODUCT#789, SK begins_with REVIEW# |
NoSQL Modelleme Prensipleri
- Agresif şekilde denormalize edin. Join’lerden kaçınmak için item’lar arasında veriyi tekrarlayın. Depolama ucuzdur; join’lerin getirdiği compute/gecikme ucuz değildir.
- Hesaplayabildiğinizi önceden hesaplayın. Okuma zamanında hesaplamak yerine, atomik
UpdateItemoperasyonları ile çalışan agregat değerleri (sayaçlar, toplamlar) sürdürün. - Önce en sık kullanılan ve gecikmeye en duyarlı erişim desenlerinize göre tasarlayın, sonra daha az sık kullanılanlara doğru geriye doğru çalışın (bunlar bazen
Scan+Filter‘a tolerans gösterebilir ya da ikincil bir analitik depoya aktarılabilir).
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?
- Tek bir
Queryile partition key’e göre birden fazla ilişkili entity tipini çekmeyi sağlar (örn. bir kullanıcının profili VE son 10 siparişini tek çağrıda getirmek) — bu, ana performans kazancıdır. - Uygulamanın yapması gereken round-trip sayısını azaltır, bu da ölçekte muazzam önem taşır (daha az network atlaması = daha düşük gecikme, daha düşük maliyet).
- DynamoDB’nin fiyatlandırma/throughput modeline uyar (tablo sayısına göre değil, tablonun indekslerine göre ödeme).
Neden Tartışmalıdır?
- Öğrenme eğrisi diktir; normalize edilmiş ilişkisel şemalara kıyasla okuması/anlaması zordur.
- Erişim desenleri baştan tam olarak bilinmiyorsa evrilmesi zordur.
- Araçlar (admin panelleri, ORM’ler) genel key/value tablolar için daha az olgundur.
- Bu desenin mimarı sayılan Rick Houlihan (AWS), bunu öncelikle yüksek ölçekli, iyi anlaşılmış OLTP sistemleri için önerir. Daha küçük uygulamalar veya gereksinimleri evrilmekte/belirsiz olan uygulamalar için çok-tablolu tasarım genellikle daha pragmatik bir seçimdir — single-table tasarımı körü körüne kopyalamayın.
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
KeyConditionExpression, taranan partition/aralığı daraltır — verimlilik buradan gelir.FilterExpression, item’lar okunduktan sonra (ve RCU tüketildikten sonra) uygulanır — döndürüleni azaltır, okunanı/faturalandırılanı değil. Performans-kritik filtreleme için aslaFilterExpression‘a güvenmeyin; bunun yerine anahtar şemasını yeniden tasarlayın.
10. Tutarlılık (Consistency) Modelleri
- Eventually Consistent (Nihai Tutarlı) Okumalar (varsayılan): en son yazmayı yansıtmayabilir; genellikle ~1 saniye içinde tutarlı hale gelir. Daha ucuzdur (güçlü okumanın yarısı RCU).
- Strongly Consistent (Güçlü Tutarlı) Okumalar: her zaman en son başarılı yazmayı yansıtır. GSI’larda mevcut değildir. Biraz daha yüksek gecikme ve maliyet (tam RCU).
- Transactional Okuma/Yazmalar: 100 item/4MB’a kadar ACID garantileri sunar, normal RCU/WCU’nun 2 katı maliyetle.
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
- Transaction başına en fazla 100 benzersiz item, toplam 4 MB.
- Veriyi değiştirmeden bir transaction katılımcısı olarak koşullu kontrolleri (
ConditionCheck) destekler — değişmezleri (invariant) zorlamak için kullanışlıdır (örn. “yalnızca kullanıcının hesabı ACTIVE ise devam et”). - Ya hep ya hiç (all-or-nothing): herhangi bir koşul başarısız olursa, tüm transaction geri alınır.
Ö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
- Gerçek çok-item değişmezleri (ödemeler, envanter rezervasyonu, benzersizlik kısıtlaması uygulama) için kullanın.
- Aşırı kullanımdan kaçının — transaction’lar 2 kat kapasite maliyetine sahiptir ve gecikme ekler. Çoğu DynamoDB iş yükü, tek-item atomik güncellemelere ve dikkatli anahtar tasarımına dayanarak transaction’lara nadiren, hatta hiç ihtiyaç duymayacak şekilde tasarlanmalıdır.
12. Batch (Toplu) İşlemler
BatchGetItem
- Tek bir çağrıda bir veya daha fazla tablodan 100 item’a (veya 16 MB’a) kadar getirir.
- Transactional değildir — kısmi başarısızlıklar, yeniden denemeniz gereken
UnprocessedKeysdöndürür. - Dahili olarak okumaları paralel çalıştırır — N adet sıralı
GetItemçağrısından daha verimlidir, ancak tüketilen toplam RCU aynıdır.
BatchWriteItem
- Çağrı başına en fazla 25 put/delete isteği, toplam 16 MB.
- Batch yazmalarda update veya koşullu destek yoktur (yalnızca
PutItem/DeleteItem,UpdateItemyok,ConditionExpressionyok). - Transactional değildir —
UnprocessedItems‘ı kontrol edin ve exponential backoff ile yeniden deneyin. - Tek bir batch isteği içindeki tekrar eden item anahtarları API tarafından doğrudan reddedilir.
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:
- Lambda tetikleyicileri (en yaygın) — neredeyse gerçek zamanlı olay tabanlı işleme.
- Kinesis Data Streams for DynamoDB — daha uzun saklama süresi (1 yıla kadar), birden fazla eşzamanlı tüketiciyi destekler, birden fazla downstream sisteme fan-out için idealdir.
Görünüm (View) Tipleri
KEYS_ONLY— yalnızca anahtar attribute’larıNEW_IMAGE— değişiklikten sonraki tüm itemOLD_IMAGE— değişiklikten önceki tüm itemNEW_AND_OLD_IMAGES— her ikisi (en esnek, en fazla depolama)
Yaygın Kullanım Alanları
- Arama indekslerine (OpenSearch/Elasticsearch) gerçek zamanlı replikasyon
- Materialized view’lar / tablolar arası denormalizasyon
- Olay tabanlı mikroservisler (yazmada domain event’leri yayınlama)
- Data lake’e Change Data Capture (CDC) (Streams → Firehose → S3)
- Denetim (audit) loglaması
- Özel Global Table benzeri kurulumlar için bölgeler/hesaplar arası replikasyon
Dikkat Edilmesi Gerekenler
- Her shard kayıtları sırayla iletir, ancak shard’lar arası sıralama garanti edilmez — kesin global sıralama önemliyse, ilişkili item’ların her zaman aynı shard’a düştüğü bir partition-key stratejisine veya bir sıralama attribute’una ihtiyacınız var.
- Lambda tetikleyicileri varsayılan olarak shard’ları paralel işler; yavaş/başarısız bir kayıt, çözülene veya retry politikası onu atlayana kadar bir shard’ı bloke edebilir (
bisectBatchOnFunctionError,maximumRetryAttemptsve bir DLQ yapılandırın).
14. Time To Live (TTL)
- Belirlenmiş bir TTL attribute’u üzerinde bir Unix epoch (milisaniye değil, saniye) zaman damgası ayarlayarak item’ları otomatik silinmek üzere işaretleyin.
- Silme işlemi anlık değildir — item’lar genellikle sürelerinin dolmasından itibaren 48 saat içinde kaldırılır (arka plan süreci), ancak fiziksel olarak silinmeden önce bile, süreleri dolar dolmaz
Query/Scan/GetItemsonuçlarından hariç tutulurlar. - Ücretsizdir — TTL silmeleri için ek WCU maliyeti yoktur.
- TTL silmeleri Streams’te görünür (sistem silmesini belirten özel bir
userIdentityalanıyla işaretlenir) — süresi dolan verileri kaybolmadan önce arşivlemek için kullanışlıdır (örn. TTL → Streams → Lambda → S3 soğuk depolama).
Yaygın Kullanımlar
- Oturum (session) verisi süre dolumu
- Geçici kilitler/lease’ler
- Cache benzeri tablolar
- Log/olay saklama pencereleri
- Bir sükunet süresinden sonra soft-delete edilmiş kayıtların otomatik temizlenmesi
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.
- Write-through cache: yazmalar DAX’a gider, DAX bunları DynamoDB’ye yazar, DAX üzerinden yapılan yazmalar için cache ve tabloyu tutarlı tutar.
- Hem item cache‘i (tekil
GetItemsonuçları) hem de query cache‘i (Query/Scansonuç kümeleri) önbelleğe alır. - Önbelleklenen veri için varsayılan TTL yapılandırılabilir (örn. 5 dakika).
- Bir VPC içinde dağıtım gerektirir; DAX istemci SDK’sı, minimal kod değişikliğiyle standart DynamoDB istemcisinin yerini alır.
- Aynı item’ların tekrar tekrar okunduğu, okuma-ağırlıklı, okuma-yoğun iş yükleri için en uygunudur (örn. ürün katalogları, liderlik tablosu okumaları).
- DAX, cache’i üzerinden yalnızca nihai tutarlı okumaları destekler — güçlü tutarlı okumalar cache’i atlayıp doğrudan DynamoDB’ye gider.
- Yazma-ağırlıklı iş yükleri veya her okumada güçlü tutarlılık gerektiren iş yükleri için uygun değildir.
16. Global Tables (Çok Bölgeli Kullanım)
Global Tables şunları sağlayan çok bölgeli, çoklu-aktif (multi-active) replikasyon sunar:
- Bölgeler arasında genellikle saniyenin altında replikasyon gecikmesi.
- Dahili zaman damgalarına dayalı son yazan kazanır (last-writer-wins) çakışma çözümü (bu yeterli değilse, uygulama seviyesi çakışma çözümü sizin sorumluluğunuzdadır).
- Her bölgenin kendi yerel okuma/yazmalarına sahip tam bir replikası vardır — dünya çapında düşük gecikmeli yerel okumalara ihtiyaç duyan global uygulamalar ve felaket kurtarma/iş sürekliliği için harikadır.
- Streams, TTL ve diğer birçok özellik, bazı hususlarla birlikte replika başına çalışır (örn. TTL silmeleri de diğer silmeler gibi replike olur).
Ne Zaman Kullanılır?
- Yerel okuma/yazma gecikmesine ihtiyaç duyan, global olarak dağılmış kullanıcı tabanları.
- Bölgesel failover / yüksek erişilebilirlik gereksinimleri.
- Yedeğin yerine geçmez — veriyi (ve hataları/silmeleri) bölgeler arasında anında replike eder; her zaman ayrı olarak Point-in-Time Recovery (PITR) veya yedekleme sürdürün.
17. Güvenlik En İyi Uygulamaları
- IAM İnce Taneli (Fine-Grained) Erişim Kontrolü: Bir çağrıcıyı (örn. Cognito ile kimliği doğrulanmış bir kullanıcıyı) yalnızca partition key’inin kendi kimliğiyle eşleştiği item’lara erişimle sınırlamak için
dynamodb:LeadingKeyskoşul anahtarlarını kullanın — çok kiracılı (multi-tenant) uygulamalar için kritiktir. - Bekleyen veri şifrelemesi (encryption at rest): varsayılan olarak etkindir (AWS’ye ait anahtar, veya ek kontrol ve denetim izi için AWS yönetilen/müşteri yönetilen KMS anahtarını seçin).
- Aktarım sırasında şifreleme: tüm API çağrıları TLS kullanır.
- VPC Endpoint’leri: VPC’nizden gelen trafiğin asla genel internete çıkmaması için DynamoDB’ye bir Gateway VPC Endpoint kullanın.
- En az ayrıcalık (least privilege): IAM politikalarını belirli aksiyonlara (
GetItem,Query) ve belirli tablolara/indekslere kapsamlayın —Resource: *üzerindedynamodb:*kullanmaktan kaçının. - Anahtarlarda son derece hassas veri saklamaktan kaçının — partition/sort key değerleri loglarda, CloudTrail olaylarında ve hata mesajlarında görünebilir.
- Özellikle hassas attribute’lar (PII, secret’lar) için DynamoDB’ye yazmadan önce alan düzeyinde şifrelemeyi (örn. AWS Encryption SDK aracılığıyla) düşünün.
18. İzleme ve Gözlemlenebilirlik
Önemli CloudWatch Metrikleri
ConsumedReadCapacityUnits/ConsumedWriteCapacityUnitsThrottledRequests/ReadThrottleEvents/WriteThrottleEventsSystemErrors/UserErrorsSuccessfulRequestLatencyConditionalCheckFailedRequests(sıçramalar optimistic-locking çekişmesine işaret edebilir)ReplicationLatency(Global Tables)
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
- Sürekli throttling için alarm kurun (
ThrottledRequests > 0art arda birkaç dakika boyunca). SuccessfulRequestLatencyp99’unun SLA eşiklerini aşması durumunda alarm kurun.- Auto Scaling’in tekrar tekrar maksimum kapasiteye ulaşması durumunda alarm kurun (düşük ayarlanmış maksimum sınırlara işaret eder).
19. Maliyet Optimizasyonu
- GSI’larda projeksiyonları doğru boyutlandırın —
KEYS_ONLYveyaINCLUDEyeterliykenALLkullanmayın. - O indeks üzerinden asla sorgulanmayacak item’ları indekslemekten kaçınmak için sparse indeksler kullanın.
- Düzenli, öngörülebilir iş yükleri için Reserved Capacity ile Provisioned + Auto Scaling’i tercih edin — ölçekte On-Demand’a göre önemli ölçüde daha ucuz olabilir.
- İhtiyacınız olmayan veriyi manuel silmek yerine sürelerini dolduran TTL’i agresif şekilde kullanın (ayrıca depolama maliyetlerinden de tasarruf sağlar ve ücretsizdir).
- Standard-IA tablo sınıfı: DynamoDB, daha düşük depolama maliyeti ancak daha yüksek throughput maliyetine sahip bir Standard-Infrequent Access tablo sınıfı sunar — büyük depolama alanına sahip ancak nispeten düşük istek oranına sahip tablolar için uygundur (örn. denetim logları, tarihi veriler).
- Batch işlemleri, birçok tekil-item çağrısına kıyasla istek ek yükünü azaltır (tüketilen toplam RCU/WCU aynı olsa da, daha az round-trip ve genellikle daha düşük Lambda/compute maliyeti vardır).
- Item boyutunu ve dolayısıyla kapasite birimi tüketimini azaltmak için büyük attribute’ları sıkıştırın (örn. büyük bir JSON blob’unu saklamadan önce gzip’leyin).
- Gereksiz güçlü tutarlılıktan kaçının — nihai tutarlı okumalar yarı maliyetlidir.
- Kritik yollarda
Scanoperasyonlarını izleyin ve ortadan kaldırın — genellikle tek başına en büyük gizli maliyet kalemidir.
20. Hata Yönetimi ve Retry Stratejileri
Yaygın Hatalar
ProvisionedThroughputExceededException— throttle ediliyorsunuz; SDK, retry edilebilir hatalar için varsayılan olarak otomatik exponential backoff ile yeniden dener, ancak sürekli throttling, anahtar şemanızı yeniden tasarlamanız veya kapasiteyi artırmanız gerektiği anlamına gelir.ConditionalCheckFailedException— koşullu bir yazma başarısız oldu (optimistic locking akışlarında beklenir — mutlaka bir hata değil, normal bir kontrol akışı sinyali olarak ele alın).TransactionCanceledException— bir transaction başarısız oldu; hangi belirli item/koşulun buna neden olduğunu görmek içinCancellationReasonsdizisini kontrol edin.ItemCollectionSizeLimitExceededException— bir LSI destekli item koleksiyonu 10 GB sınırını aştı.ValidationException— hatalı biçimlendirilmiş istek (kötü expression sözdizimi, yanlış veri tipi vb.).
Retry Stratejisi
- Modern AWS SDK’ların tümü, retry edilebilir hatalar için varsayılan olarak jitter’lı exponential backoff uygular — jitter olmadan kendi naif retry döngünüzü oluşturmayın; birçok istemcide senkronize retry’lar “retry storm"lara neden olabilir.
BatchWriteItem/BatchGetItemiçin,UnprocessedItems/UnprocessedKeys‘i açıkça yeniden göndermeniz gerekir — SDK, bunu batch seviyesinde sizin için otomatik olarak yapmaz (yalnızca tüm-istek seviyesindeki throttling retry’sı için yapar).
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-Pattern | Neden Sorun? | Çözüm |
|---|---|---|
Kritik istek yolunda Scan kullanmak | Tüm tabloyu okur, muazzam RCU tüketir, ölçekte yavaştır | Bir 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 gerektirir | Denormalize 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ır | Yüksek kardinaliteli bir bileşen ekleyin veya anahtarı shard’layın |
| Büyük blob’ları (>400 KB) doğrudan saklamak | Item boyut limitini aşar | S3’te saklayın, DynamoDB’de bir pointer/referans tutun |
Performans için FilterExpression‘a güvenmek | Filtreleme okumadan sonra gerçekleşir — taranan item’lar için yine de tam RCU ödersiniz | Filtrelemenin KeyConditionExpression üzerinden gerçekleşmesi için anahtar şemasını yeniden tasarlayın |
| Transaction’ları aşırı kullanmak | 2 kat kapasite maliyeti, eklenen gecikme, eklenen karmaşıklık | Yalnızca gerçek çok-item değişmezleri için kullanın |
Batch çağrılarda UnprocessedItems/UnprocessedKeys‘i görmezden gelmek | Kı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 zorlar | Baş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ır | Bunun yerine bir GSI kullanın veya partition key’i shard’layın |
23. Yedekleme, Geri Yükleme ve Migrasyon
- Point-in-Time Recovery (PITR): son 35 güne kadar herhangi bir saniyeye geri yüklemeyi sağlayan sürekli yedeklemeler. Geri yükleme yeni bir tablo oluşturur — mevcut tablonun üzerine yazmaz.
- On-Demand (İsteğe Bağlı) Yedeklemeler: açıkça silinene kadar saklanan manuel, tam yedeklemeler; riskli şema/veri migrasyonlarından önce kullanışlıdır.
- S3’e Aktarma (Export): tablo verisini (tam veya bir PITR noktasından) DynamoDB JSON veya Amazon Ion formatında, tablo okuma kapasitesi tüketmeden S3’e aktarın — analitik pipeline’lar (Athena, EMR) veya arşivleme için idealdir.
- S3’ten İçe Aktarma (Import): S3’ten (CSV, DynamoDB JSON, Amazon Ion) yeni bir tabloya toplu veri yükleyin.
- Şema değişiklikleri için migrasyon stratejisi: DynamoDB primary key dışında şemasız olduğundan, çoğu “şema migrasyonu” aslında veri migrasyonudur — yeni-şekilli item’ları eski olanların yanına yazın, bir Scan+dönüştürme scripti veya Streams-tabanlı backfill Lambda’sı ile geriye dönük doldurun, ardından backfill tamamlandığında okumaları geçirin.
24. En İyi Uygulamalar Kontrol Listesi
- Erişim desenleri, tablo tasarımından önce belirlendi ve dokümante edildi
- Partition key yüksek kardinaliteye ve eşit erişim dağılımına sahip
- Sort key, gerekli olduğunda aralık sorgularını/hiyerarşiyi destekleyecek şekilde tasarlandı
- Alternatif erişim desenleri için GSI’lar kullanıldı; filtreleme gerektiğinde sparse indeksler kullanıldı
- LSI’lar yalnızca güçlü tutarlılık + alternatif sıralama düzeni gerçekten gerekli olduğunda, 10 GB/partition limiti göz önünde bulundurularak kullanıldı
- Kritik/production istek yollarında
Scanoperasyonu yok - Uygun kapasite modu seçildi (yeni/öngörülemeyen için On-Demand, düzenli durum için Provisioned+AutoScaling)
- Süresi dolabilecek veriler için TTL yapılandırıldı
- Olay-tabanlı downstream işleme gerekiyorsa Streams bağlandı
- Okuma-ağırlıklı, gecikmeye kritik iş yükleri için DAX değerlendirildi
- IAM politikaları en az ayrıcalıkla kapsamlandı, çok-kiracılı izolasyon için
LeadingKeyskoşulu kullanıldı - Production tablolarında PITR etkinleştirildi
- Throttling ve gecikme için CloudWatch alarmları yapılandırıldı
- Batch operasyon kısmi başarısızlıkları için retry mantığı mevcut
- Item boyutları izleniyor; büyük blob’lar S3’e aktarıldı
- Maliyet periyodik olarak gözden geçiriliyor (kapasite modu, projeksiyonlar, tablo sınıfı)
İleri Okuma
- AWS Dokümantasyonu: Amazon DynamoDB Developer Guide
- Alex DeBrie tarafından yazılan The DynamoDB Book
- Rick Houlihan’ın Advanced NoSQL Design Patterns hakkındaki AWS re:Invent konuşmaları