AWS DynamoDB Kurulum ve Yönetimi
Bulut altyapısı yönetirken en çok baş ağrıtan konulardan biri veri tabanı seçimi ve yönetimidir. İlişkisel veri tabanları her zaman ideal çözüm değildir; özellikle yüksek ölçeklenebilirlik, düşük gecikme süresi ve esnek şema gerektiren uygulamalarda NoSQL çözümleri öne çıkar. AWS DynamoDB tam da bu ihtiyaçları karşılamak üzere tasarlanmış, tamamen yönetilen bir NoSQL veri tabanı servisidir. E-ticaret platformlarından oyun uygulamalarına, IoT çözümlerinden gerçek zamanlı analitik sistemlerine kadar pek çok alanda kullanılan DynamoDB’yi doğru kurmak ve yönetmek, hem performans hem de maliyet açısından kritik öneme sahiptir.
DynamoDB Temelleri
DynamoDB’yi anlamak için önce temel kavramları netleştirmek gerekiyor. İlişkisel veri tabanlarından farklı bir terminoloji kullanıyor ve bu farkları kavramadan yapılan kurulumlar ileride ciddi sorunlara yol açabiliyor.
Tablo (Table): DynamoDB’de veriler tablolarda saklanır. Her tablo bir isimle tanımlanır ve AWS hesabı + bölge bazında benzersiz olmalıdır.
Öğe (Item): Tablodaki her kayıt bir öğedir. İlişkisel veri tabanındaki satır kavramına karşılık gelir. Her öğe maksimum 400 KB boyutunda olabilir.
Nitelik (Attribute): Öğelerin sahip olduğu veri alanlarıdır. Sütun kavramına benzer ancak DynamoDB’de şema esnektir; her öğe farklı niteliklere sahip olabilir.
Birincil Anahtar (Primary Key): Her öğeyi benzersiz olarak tanımlar. İki türü vardır:
- Partition Key (Hash Key): Tek nitelikten oluşan birincil anahtar. DynamoDB bu değeri hash fonksiyonundan geçirerek veriyi hangi fiziksel bölüme (partition) yazacağını belirler.
- Composite Key: Partition Key + Sort Key kombinasyonudur. Aynı partition key’e sahip öğeler sort key’e göre sıralı tutulur.
İndeksler: Birincil anahtar dışındaki alanlar üzerinden sorgulama yapabilmek için kullanılır. Global Secondary Index (GSI) ve Local Secondary Index (LSI) olmak üzere iki türü vardır.
AWS CLI ile DynamoDB Kurulumu
Önce AWS CLI’ın düzgün yapılandırıldığından emin olalım:
# AWS CLI kurulumu (Ubuntu/Debian)
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
# Kimlik bilgilerini yapılandır
aws configure
# AWS Access Key ID: [ACCESS_KEY]
# AWS Secret Access Key: [SECRET_KEY]
# Default region name: eu-west-1
# Default output format: json
# Yapılandırmayı doğrula
aws sts get-caller-identity
Şimdi ilk DynamoDB tablomuz olan bir e-ticaret sipariş tablosunu oluşturalım:
# Sipariş tablosu oluşturma
aws dynamodb create-table
--table-name Orders
--attribute-definitions
AttributeName=CustomerId,AttributeType=S
AttributeName=OrderId,AttributeType=S
AttributeName=OrderDate,AttributeType=S
--key-schema
AttributeName=CustomerId,KeyType=HASH
AttributeName=OrderId,KeyType=RANGE
--billing-mode PAY_PER_REQUEST
--global-secondary-indexes
"[{
"IndexName": "OrderDate-index",
"KeySchema": [{"AttributeName":"OrderDate","KeyType":"HASH"}],
"Projection":{"ProjectionType":"ALL"}
}]"
--tags Key=Environment,Value=Production Key=Team,Value=Backend
--region eu-west-1
# Tablo oluşturuldu mu kontrol et
aws dynamodb describe-table
--table-name Orders
--region eu-west-1
--query 'Table.TableStatus'
Burada PAY_PER_REQUEST billing modunu tercih ettim. Yeni başlayan projeler veya tahmin edilemeyen trafik deseni olan uygulamalar için idealdir. Sabit ve yüksek trafik varsa PROVISIONED mod daha ekonomik olabilir.
Kapasite Yönetimi
Kapasite yönetimi DynamoDB’nin en kritik konularından biridir. Yanlış yapılandırma hem aşırı maliyet hem de performans sorunlarına yol açar.
Provisioned Kapasite Modu
Öngörülebilir iş yükü olan, trafik grafikleri belli olan uygulamalar için provisioned mod kullanmak çok daha ekonomik olabilir. Bir müşteri hizmetleri platformu için düşünelim: Gün içinde 09:00-18:00 arası yoğun, gece saatleri ise neredeyse boş.
# Provisioned kapasite ile tablo oluşturma
aws dynamodb create-table
--table-name CustomerSupport
--attribute-definitions
AttributeName=TicketId,AttributeType=S
AttributeName=CustomerId,AttributeType=S
--key-schema
AttributeName=TicketId,KeyType=HASH
AttributeName=CustomerId,KeyType=RANGE
--billing-mode PROVISIONED
--provisioned-throughput ReadCapacityUnits=100,WriteCapacityUnits=50
--region eu-west-1
# Auto Scaling politikası tanımlama
aws application-autoscaling register-scalable-target
--service-namespace dynamodb
--resource-id "table/CustomerSupport"
--scalable-dimension "dynamodb:table:ReadCapacityUnits"
--min-capacity 20
--max-capacity 200
# Scaling politikası oluşturma
aws application-autoscaling put-scaling-policy
--service-namespace dynamodb
--resource-id "table/CustomerSupport"
--scalable-dimension "dynamodb:table:ReadCapacityUnits"
--policy-name "CustomerSupport-ReadScaling"
--policy-type "TargetTrackingScaling"
--target-tracking-scaling-policy-configuration
'{"TargetValue": 70.0, "PredefinedMetricSpecification": {"PredefinedMetricType": "DynamoDBReadCapacityUtilization"}}'
Bu yapılandırma ile okuma kapasitesi kullanımı yüzde 70’i aştığında otomatik olarak ölçeklenir, yüzde 70’in altına düştüğünde ise küçülür.
Veri İşlemleri
Gerçek dünya senaryolarında DynamoDB ile veri yazma ve okuma işlemlerini ele alalım. Python boto3 kütüphanesi ile çalışmak CLI’a göre çok daha pratiktir.
# boto3 kurulumu
pip3 install boto3
# Bağlantı testi scripti
cat << 'EOF' > test_connection.py
import boto3
from botocore.exceptions import ClientError
dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
def test_connection():
try:
client = boto3.client('dynamodb', region_name='eu-west-1')
response = client.list_tables()
print("Bağlantı başarılı!")
print(f"Mevcut tablolar: {response['TableNames']}")
except ClientError as e:
print(f"Bağlantı hatası: {e.response['Error']['Message']}")
if __name__ == "__main__":
test_connection()
EOF
python3 test_connection.py
Toplu Veri Yazma
Gerçek projede genellikle toplu veri yükleme ihtiyacı doğar. Örneğin eski bir SQL veri tabanından geçiş yapıyorsunuz:
import boto3
import json
from decimal import Decimal
dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
table = dynamodb.Table('Orders')
def bulk_write_orders(orders_data):
"""
Toplu sipariş yazma - batch_writer 25 öğelik gruplarda yazar
ve başarısız öğeleri otomatik olarak tekrar dener
"""
with table.batch_writer() as batch:
for order in orders_data:
batch.put_item(
Item={
'CustomerId': order['customer_id'],
'OrderId': order['order_id'],
'OrderDate': order['order_date'],
'TotalAmount': Decimal(str(order['total_amount'])),
'Status': order['status'],
'Items': order['items']
}
)
print(f"{len(orders_data)} sipariş başarıyla yazıldı.")
# Örnek kullanım
sample_orders = [
{
'customer_id': 'CUST-001',
'order_id': 'ORD-2024-001',
'order_date': '2024-01-15',
'total_amount': 459.99,
'status': 'DELIVERED',
'items': ['laptop-stand', 'keyboard']
},
{
'customer_id': 'CUST-002',
'order_id': 'ORD-2024-002',
'order_date': '2024-01-16',
'total_amount': 89.50,
'status': 'PENDING',
'items': ['mouse-pad']
}
]
bulk_write_orders(sample_orders)
Sorgulama ve Filtreleme
DynamoDB’de Query ve Scan arasındaki farkı kavramak çok önemlidir. Scan tüm tabloyu tarar ve maliyetlidir; mümkün olduğunca Query kullanılmalıdır.
import boto3
from boto3.dynamodb.conditions import Key, Attr
from decimal import Decimal
dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
table = dynamodb.Table('Orders')
def get_customer_orders(customer_id, start_date=None, end_date=None):
"""Belirli bir müşterinin siparişlerini getirir"""
key_condition = Key('CustomerId').eq(customer_id)
if start_date and end_date:
key_condition = key_condition & Key('OrderId').between(
f'ORD-{start_date}',
f'ORD-{end_date}'
)
response = table.query(
KeyConditionExpression=key_condition,
FilterExpression=Attr('Status').ne('CANCELLED'),
ProjectionExpression='OrderId, OrderDate, TotalAmount, #st',
ExpressionAttributeNames={'#st': 'Status'},
ScanIndexForward=False # En yeni siparişler önce gelsin
)
orders = response['Items']
# Pagination - büyük veri setleri için
while 'LastEvaluatedKey' in response:
response = table.query(
KeyConditionExpression=key_condition,
ExclusiveStartKey=response['LastEvaluatedKey']
)
orders.extend(response['Items'])
return orders
# GSI üzerinden sorgulama
def get_orders_by_date(order_date):
"""Belirli tarihteki tüm siparişleri GSI üzerinden getirir"""
response = table.query(
IndexName='OrderDate-index',
KeyConditionExpression=Key('OrderDate').eq(order_date)
)
return response['Items']
# Kullanım
orders = get_customer_orders('CUST-001')
print(f"Müşteri siparişleri: {len(orders)} adet")
DynamoDB Streams ve Lambda Entegrasyonu
Gerçek dünya uygulamalarında DynamoDB değişikliklerini yakalamak ve başka sistemlere iletmek sık ihtiyaç duyulan bir senaryodur. Örneğin sipariş durumu değiştiğinde müşteriyi bilgilendirmek:
# DynamoDB Streams etkinleştirme
aws dynamodb update-table
--table-name Orders
--stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
--region eu-west-1
# Stream ARN'ini al
STREAM_ARN=$(aws dynamodb describe-table
--table-name Orders
--region eu-west-1
--query 'Table.LatestStreamArn'
--output text)
echo "Stream ARN: $STREAM_ARN"
# Lambda fonksiyonu için event source mapping oluştur
aws lambda create-event-source-mapping
--function-name order-status-notifier
--event-source-arn $STREAM_ARN
--starting-position LATEST
--batch-size 10
--maximum-retry-attempts 3
Yedekleme ve Geri Yükleme
Üretim ortamında yedekleme stratejisi hayati önem taşır. DynamoDB iki tür yedekleme sunar: On-Demand Backup ve Point-in-Time Recovery (PITR).
# Point-in-Time Recovery etkinleştirme (önerilen - her zaman açık olsun)
aws dynamodb update-continuous-backups
--table-name Orders
--point-in-time-recovery-specification PointInTimeRecoveryEnabled=true
--region eu-west-1
# Manuel on-demand yedek alma
aws dynamodb create-backup
--table-name Orders
--backup-name "Orders-backup-$(date +%Y%m%d-%H%M%S)"
--region eu-west-1
# Mevcut yedekleri listele
aws dynamodb list-backups
--table-name Orders
--region eu-west-1
--query 'BackupSummaries[*].[BackupName,BackupStatus,BackupCreationDateTime]'
--output table
# Belirli bir zamana geri yükleme (PITR)
aws dynamodb restore-table-to-point-in-time
--source-table-name Orders
--target-table-name Orders-restored
--restore-date-time "2024-01-15T10:30:00Z"
--region eu-west-1
# Yedekten geri yükleme durumunu izle
aws dynamodb describe-table
--table-name Orders-restored
--region eu-west-1
--query 'Table.[TableStatus,RestoreSummary]'
Yedekleme için bir cron job kurmanı öneririm. Her gece otomatik yedek alarak beklenmedik durumlar için hazırlıklı olabilirsin:
#!/bin/bash
# /usr/local/bin/dynamodb-backup.sh
TABLES=("Orders" "CustomerSupport" "Products")
REGION="eu-west-1"
DATE=$(date +%Y%m%d)
LOG_FILE="/var/log/dynamodb-backup.log"
echo "$(date): DynamoDB yedekleme başladı" >> $LOG_FILE
for TABLE in "${TABLES[@]}"; do
BACKUP_NAME="${TABLE}-backup-${DATE}"
aws dynamodb create-backup
--table-name "$TABLE"
--backup-name "$BACKUP_NAME"
--region "$REGION" >> $LOG_FILE 2>&1
if [ $? -eq 0 ]; then
echo "$(date): $TABLE yedeği başarıyla alındı: $BACKUP_NAME" >> $LOG_FILE
else
echo "$(date): HATA - $TABLE yedeği alınamadı!" >> $LOG_FILE
# Alarm gönder
aws sns publish
--topic-arn "arn:aws:sns:eu-west-1:123456789:alerts"
--message "DynamoDB yedekleme hatası: $TABLE"
--region "$REGION"
fi
done
# 30 günden eski yedekleri temizle
aws dynamodb list-backups
--region "$REGION"
--time-range-upper-bound $(date -d "30 days ago" +%s)
--query 'BackupSummaries[*].BackupArn'
--output text | while read ARN; do
aws dynamodb delete-backup --backup-arn "$ARN" --region "$REGION"
echo "$(date): Eski yedek silindi: $ARN" >> $LOG_FILE
done
echo "$(date): DynamoDB yedekleme tamamlandı" >> $LOG_FILE
Crontab’a ekle:
# Her gece 02:00'de yedek al
echo "0 2 * * * /usr/local/bin/dynamodb-backup.sh" | crontab -
İzleme ve Alarm Kurulumu
DynamoDB tablolarını CloudWatch ile izlemek standart bir pratik olmalıdır. Özellikle ThrottledRequests, SystemErrors ve ConsumedReadCapacityUnits metriklerini takip etmek kritiktir.
# Throttling alarmı oluşturma
aws cloudwatch put-metric-alarm
--alarm-name "DynamoDB-Orders-ReadThrottle"
--alarm-description "Orders tablosunda okuma kısıtlaması var"
--metric-name ThrottledRequests
--namespace AWS/DynamoDB
--statistic Sum
--dimensions Name=TableName,Value=Orders Name=Operation,Value=GetItem
--period 60
--evaluation-periods 5
--threshold 10
--comparison-operator GreaterThanThreshold
--alarm-actions "arn:aws:sns:eu-west-1:123456789:alerts"
--treat-missing-data notBreaching
--region eu-west-1
# Sistem hata alarmı
aws cloudwatch put-metric-alarm
--alarm-name "DynamoDB-Orders-SystemErrors"
--alarm-description "Orders tablosunda sistem hatası"
--metric-name SystemErrors
--namespace AWS/DynamoDB
--statistic Sum
--dimensions Name=TableName,Value=Orders
--period 60
--evaluation-periods 2
--threshold 1
--comparison-operator GreaterThanOrEqualToThreshold
--alarm-actions "arn:aws:sns:eu-west-1:123456789:alerts"
--region eu-west-1
DAX ile Önbellekleme
Uygulamanın okuma yoğun olduğu ve gecikme süresinin kritik önem taşıdığı durumlarda DynamoDB Accelerator (DAX) kullanmak mantıklıdır. DAX, milisaniye düzeyindeki gecikmeyi mikrosaniye düzeyine indirebilir.
# DAX subnet group oluşturma
aws dax create-subnet-group
--subnet-group-name orders-dax-subnet
--subnet-ids subnet-abc123 subnet-def456
--description "DAX subnet group for Orders"
--region eu-west-1
# DAX cluster oluşturma
aws dax create-cluster
--cluster-name orders-dax
--node-type dax.r5.large
--replication-factor 3
--iam-role-arn "arn:aws:iam::123456789:role/DAXRole"
--subnet-group-name orders-dax-subnet
--security-group-ids sg-xyz789
--region eu-west-1
DAX özellikle aşağıdaki senaryolarda tercih edilir:
- Aynı veri sık sık okunuyorsa (ürün kataloğu, konfigürasyon verileri)
- Okuma yoğunluğu çok yüksekse
- Gecikme süresi kritikse (oyun uygulamaları, gerçek zamanlı dashboard)
Güvenlik En İyi Pratikleri
DynamoDB güvenliğinde dikkat edilmesi gereken başlıca noktalar şunlardır:
IAM politikaları en az yetki prensibiyle yapılandırılmalıdır. Her servis sadece ihtiyaç duyduğu tablolara ve işlemlere erişebilmelidir:
# Sadece okuma yetkisi olan IAM politikası
cat << 'EOF' > dynamodb-readonly-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:BatchGetItem",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": [
"arn:aws:dynamodb:eu-west-1:123456789:table/Orders",
"arn:aws:dynamodb:eu-west-1:123456789:table/Orders/index/*"
],
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "eu-west-1"
}
}
}
]
}
EOF
aws iam create-policy
--policy-name DynamoDB-Orders-ReadOnly
--policy-document file://dynamodb-readonly-policy.json
Encryption at rest varsayılan olarak etkindir ve AWS managed key kullanır. Daha fazla kontrol için kendi KMS anahtarını kullanabilirsin:
# Müşteri yönetimli KMS anahtarıyla şifreleme
aws dynamodb create-table
--table-name SensitiveData
--attribute-definitions AttributeName=UserId,AttributeType=S
--key-schema AttributeName=UserId,KeyType=HASH
--billing-mode PAY_PER_REQUEST
--sse-specification Enabled=true,SSEType=KMS,KMSMasterKeyId=arn:aws:kms:eu-west-1:123456789:key/mrk-abc123
--region eu-west-1
Yaygın Sorunlar ve Çözümleri
Hot Partition sorunu: Tüm yazma işlemleri aynı partition key’e yönelik olduğunda o bölüm aşırı yüklenir. Çözüm olarak partition key’e rastgele son ek eklemek (key sharding) veya write sharding kullanmak gerekir.
ProvisionedThroughputExceededException: Kapasite limitine ulaşıldığında fırlatılır. Auto scaling aktifse kendiliğinden çözülür, değilse kapasiteyi artırmak ya da istek hızını düşürmek gerekir. Uygulamada exponential backoff ile retry mantığı mutlaka olmalıdır.
Büyük öğe sorunları: 400 KB limitini aşan veriler için S3’e referans saklamak en temiz yaklaşımdır. DynamoDB’de S3 nesnesinin anahtarını, asıl veriyi ise S3’te tut.
Scan kullanımı: Büyük tablolarda scan yapmak hem yavaş hem de maliyetlidir. Sorgu desenlerini baştan belirleyerek gerekli indeksleri tasarlamak en doğru yaklaşımdır.
Sonuç
DynamoDB, doğru kullanıldığında gerçekten güçlü bir servis. Ancak ilişkisel veri tabanı zihniyetiyle yaklaşıldığında ciddi sorunlara kapı aralıyor. En kritik tavsiyem şu: sorgu desenlerini tasarım aşamasında belirle ve tablo yapısını bu desenlere göre oluştur. İlişkisel veri tabanlarının aksine DynamoDB’de sonradan indeks eklemek ya da şema değiştirmek çok daha maliyetli ve zahmetlidir.
Maliyet optimizasyonu için PAY_PER_REQUEST modu ile başla, trafik deseni netleştikten sonra provisioned moda geçip auto scaling ayarla. PITR’ı her zaman açık tut, bu sigortanı boşa çıkarmaz. IAM politikalarını en az yetki prensibiyle yapılandır ve CloudWatch alarmlarını kur.
Gerçek dünya projelerinde DynamoDB ile en çok sorun yaşanan nokta hot partition problemidir; partition key seçimine özellikle dikkat et. Yüksek kardinalite (benzersiz değer çeşitliliği) olan alanları partition key olarak seçmek, trafiğin partitionlar arasında dengeli dağılmasını sağlar.
Son olarak: DynamoDB her uygulama için doğru seçim değildir. Karmaşık ilişkisel sorgular, ACID işlemleri veya ad hoc analitik sorgular için PostgreSQL ya da Aurora gibi seçenekler çok daha uygun olabilir. Doğru aracı doğru iş için kullanmak, iyi bir sysadmin’in en temel refleksidir.
