a grassy field under a blue sky with clouds — Azure Cosmos DB: Genel Olarak Dağıtılmış NoSQL Veritabanı
Azure

Bir IoT platformu müşterimiz Türkiye + Almanya + UAE + Singapur’da 1.2 milyon sensörden saniyede 18.000 telemetry message alıyor. Container’da 4 milyar document, aktif aylık 180 GB write. Cosmos DB faturası başlangıçta $34.000/ay’a çıkmıştı. 3 ay süren optimizasyon sonunda $11.200/ay. Bu yazı o projeden notlar.

API Seçimi: NoSQL, MongoDB, Cassandra, Gremlin, Table

API Tipik kullanım Notlar
NoSQL (Core SQL) Yeni proje, full Cosmos DB feature En geniş feature set
MongoDB Mevcut Mongo uygulaması migrate 4.0/4.2/5.0/6.0 wire protocol
Cassandra Mevcut Cassandra workload CQL desteği
Gremlin Graph database (knowledge graph, fraud) Apache TinkerPop
Table Azure Table Storage migrate, basit key-value Cheap option

IoT müşterimiz mevcut Mongo uygulamasından geliyordu, MongoDB API ile başladı. Wire protocol uyumlu, kod değişikliği minimum.

Partition Key Tasarımı: En Kritik Karar

Yanlış partition key = pahalı + yavaş + scale etmeyen. Doğru partition key = ucuz + hızlı + sınırsız scale.

Kötü örnek: partitionKey: deviceId. 1.2 milyon farklı device → 1.2 milyon partition. Aggregation query’ler tüm partition’lara fan-out, RU patlıyor.

İyi örnek: partitionKey: deviceCustomerId_yyyymm (müşteri × ay bazlı bucket). 600 müşteri × 12 ay = 7.200 partition. Bir müşterinin bir aylık tüm data’sı tek partition’da, query verimli.

Gerçek partition key seçimi:

{
  "deviceId": "iot-tr-bursa-otomotiv-001-sensor-temp",
  "partitionKey": "tr-bursa-otomotiv-001:202604",  // tenant + ay
  "ts": "2026-04-24T18:30:00Z",
  "value": 23.4,
  "unit": "C",
  "metadata": {...}
}

Sorgu pattern: “Bu müşterinin nisan ayındaki tüm sensör verisi” → tek partition tarama, 5 RU. Yanlış partition’da aynı query 4.500 RU’ya çıkıyordu.

RU/s Capacity Mode

Mode Avantaj Dezavantaj
Provisioned (manual) Predictable cost Atıl kapasite veya throttle
Autoscale Talep’e göre 10x scale Min %10, max RU/s × $1/100 RU/s/ay
Serverless Pay-per-request Max 5.000 RU/s, geo-replication yok

IoT müşterimiz başta Provisioned 100.000 RU/s set etmişti, gece düşük load’da %85 atıl. Autoscale 10.000-100.000 RU/s’e geçince fatura %42 düştü.

Multi-Region Write: Active-Active Topology

Müşteri 4 lokasyonda yazıyor. Single-write region (West Europe) tek noktada bottleneck + cross-continent latency. Multi-region write açıldı:

az cosmosdb update 
  --resource-group rg-iot 
  --name cosmos-iot-prod 
  --enable-multiple-write-locations true 
  --locations regionName=westeurope failoverPriority=0 
  --locations regionName=germanywestcentral failoverPriority=1 
  --locations regionName=uaenorth failoverPriority=2 
  --locations regionName=southeastasia failoverPriority=3

Her bölgedeki client kendi en yakın region’ına yazıyor. Latency 180 ms → 12 ms.

Conflict resolution: timestamp-based (last writer wins) tercih edildi. IoT telemetry data’da conflict çok nadir, son yazılan değer doğru.

Consistency Level: Performance vs Consistency Trade-off

Level Consistency Latency RU cost
Strong Anlık tüm region’lar tutarlı Yüksek 2x
Bounded staleness K versiyon veya T zaman geride Orta 2x
Session Kullanıcı kendi yazdığını okuyor Düşük 1x (default)
Consistent prefix Sıralı ama gecikmeli Düşük 1x
Eventual En yüksek perf, en zayıf consistency En düşük 1x

IoT müşterimizde Eventual consistency yeterli — telemetry data, bir saniyelik gecikme business etki yaratmaz. Default Session’dan Eventual’a geçiş %18 RU tasarrufu.

TTL: Otomatik Veri Silme ile Storage Maliyetini Düşürme

Detail telemetry 90 gün sonra silinir, sadece aggregated data kalır. Container TTL = 7.776.000 saniye (90 gün), document’larda _ts field’ı bazlı otomatik temizlik.

az cosmosdb mongodb collection update 
  --account-name cosmos-iot-prod 
  --resource-group rg-iot 
  --database-name iot 
  --name telemetry 
  --idx '[{"key":{"keys":["_ts"]},"options":{"expireAfterSeconds":7776000}}]'

Storage 480 TB’dan 180 TB’a düştü, aylık $7.200 storage tasarrufu.

Index Optimization

Default’ta tüm property’ler index’e dahil. Çoğu IoT field’ı asla query’de WHERE/ORDER BY’a girmiyor. Selective indexing:

{
  "indexingMode": "consistent",
  "includedPaths": [
    {"path": "/partitionKey/?"},
    {"path": "/ts/?"},
    {"path": "/deviceId/?"},
    {"path": "/value/?"}
  ],
  "excludedPaths": [
    {"path": "/metadata/*"},  // metadata içi tüm field'lar excluded
    {"path": "/raw/*"}
  ]
}

Index storage %78 azaldı, write RU %35 azaldı.

Change Feed ile Real-time Stream Processing

Cosmos DB Change Feed ile her yeni document Azure Functions / Stream Analytics’e push. Müşteri kullanım: anomaly detection (sensör değeri normal range dışı → alert).

[FunctionName("AnomalyDetector")]
public static async Task Run(
    [CosmosDBTrigger(
        databaseName: "iot",
        containerName: "telemetry",
        Connection = "CosmosDBConnection",
        LeaseContainerName = "leases",
        CreateLeaseContainerIfNotExists = true)]
    IReadOnlyList<TelemetryDoc> input,
    ILogger log)
{
    foreach (var doc in input)
    {
        if (doc.Value > doc.Threshold)
            await SendAlert(doc.DeviceId, doc.Value);
    }
}

Sahada Düşülen Üç Tuzak

  1. Partition key’i değiştirmeye çalışmak: Mevcut container’da partition key değiştirilemiyor. Yeni container oluşturup migrate gerekiyor. İlk seferde doğru tasarla.
  2. Strong consistency’i default tutmak: 2x RU. Real ihtiyaç incelenmeden Strong seçilmiş, bütçe heba.
  3. Cross-partition query yapmak: SELECT * FROM c WHERE c.deviceId = X (deviceId partition key değilse) tüm partition’ları tarar. Partition key WHERE clause’a koy.

Sonuçlar

Metrik Önceki Sonraki
Aylık fatura $34.000 $11.200
P99 read latency 180 ms 12 ms
P99 write latency 240 ms 18 ms
Storage 480 TB 180 TB
Throttle (429) rate %2.3 %0.04

CloudSpark olarak Cosmos DB partition key tasarımı, multi-region topology, RU/s optimization, MongoDB migration ve performance tuning projeleri için danışmanlık veriyoruz.

🇹🇷 Türkçe🇬🇧 English🇩🇪 Deutsch🇫🇷 Français🇸🇦 العربية🇷🇺 Русский🇪🇸 Español