İçeriğe atla

2025-09-08

Veritabanı Query Profiling: Sistematik Optimizasyon Yolculuğu

Sistematik veritabanı profiling ve optimizasyonu ile yıllık altyapı maliyetlerini 100K dolar azalttığımız hikaye. PostgreSQL ve MongoDB performance deneyimleri.

Üç aylık maliyet değerlendirmelerinde veritabanı altyapısının orantısız kaynak tükettiği sıkça görülür. Aylık maliyetler 5.400 dolara ulaşabilir, query’ler yoğun trafik sırasında timeout alabilir ve varsayılan “çözüm” daha fazla sunucu eklemek olabilir.

Veritabanı problemlerine donanım eklemek kök nedenleri nadiren çözer; pahalı ve şaşırtıcı derecede etkisiz bir yaklaşımdır.

Bu yazı, PostgreSQL ve MongoDB ortamlarında yıllık 64.800 dolardan 28.800 dolara düşen maliyetle sonuçlanan sistematik veritabanı optimizasyon tekniklerini inceliyor.

Problem: Performance Gerçekle Karşılaştığında

PostgreSQL (işlemsel veri için) ve MongoDB (analitik ve döküman depolaması için) kullanan çok kiracılı bir SaaS platformunu ele alalım. Mimari kağıt üzerinde sağlam görünüyor. Ancak pratikte ürün arama query’leri yoğun trafik dönemlerinde 8 saniye sürüyor.

Kriz anı genellikle kritik bir sunum sırasında gelir: müşteri analitiği dashboard’u 45 saniyede yüklendiğinde paydaşların önünde. Bu an, yama çözümlerinden fazlasına ihtiyaç olduğunu açıkça gösterir. Veritabanlarının içinde gerçekte neler olduğunu anlamak için sistematik bir yaklaşım gerekir.

Olmayan Büyük MongoDB Migration’ı

Yaygın bir hata kalıbı: MongoDB’nin tüm ölçeklendirme sorunlarını çözeceği varsayımıyla ana ürün kataloğunun PostgreSQL’den MongoDB’ye taşınması. “NoSQL büyümeyi daha iyi handle eder, döküman depolaması API response’larına mükemmel uyar” gerekçesiyle alınan bu karar çoğunlukla üç ay sonra 2 kat daha yüksek AWS faturalarıyla sonuçlanır.

SQL düşüncesini NoSQL dünyasına taşımak sorunun temelidir: uygulama, MongoDB aggregation pipeline’larında yapılması gereken karmaşık join’leri memory’de gerçekleştirir. Query performansı öncekinden 3 kat daha kötü olabilir.

Veritabanı teknolojisi migration’ları kötü tasarlanmış query’leri sihirli bir şekilde düzeltmez. Önce erişim pattern’larını anlamak, sonra doğru aracı seçmek bu maliyetli rotadan kaçındırır.

Sistematik Profiling Framework Kurma

Sonraki adım, performansın ve maliyetin tam olarak nereye gittiğini gösterecek kapsamlı bir profiling sistemi kurmak: PostgreSQL ve MongoDB’de gerçek tablo.

PostgreSQL Profiling Stack

100ms’den fazla süren her şeyi yakalamak için detaylı query logging etkinleştirilir:

-- Kapsamlı query logging'i etkinleştir
ALTER SYSTEM SET log_min_duration_statement = 100;
ALTER SYSTEM SET log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h ';
ALTER SYSTEM SET log_checkpoints = on;
ALTER SYSTEM SET log_connections = on;
ALTER SYSTEM SET log_disconnections = on;
ALTER SYSTEM SET log_lock_waits = on;
SELECT pg_reload_conf();

pg_stat_statements ile gerçek tablo ortaya çıkar:

-- Kaynak yiyen query'leri bul
SELECT 
    substring(query, 1, 100) as query_start,
    calls,
    total_time,
    mean_time,
    rows,
    100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0) AS cache_hit_percent
FROM pg_stat_statements 
ORDER BY total_time DESC 
LIMIT 20;

Sonuçlar göz açıcıydı. En üstteki query’imiz toplam veritabanı zamanının %30’unu tüketiyordu ve cache hit oranı sadece %12’ydi. Birisi ürünleri kategoriye göre filtrelediğinde 50 milyonluk tabloyu her seferinde scan ediyordu.

MongoDB Profiling Configuration

MongoDB için profiler 100ms üzerindeki operasyonları yakalar:

// Yavaş operasyonlar için profiler'ı etkinleştir
db.setProfilingLevel(2, { slowms: 100 });

// Pattern'ları analiz et
db.system.profile.aggregate([
  { $match: { ns: "myapp.products" } },
  { $group: {
      _id: "$command.find",
      count: { $sum: 1 },
      avgDuration: { $avg: "$millis" },
      maxDuration: { $max: "$millis" },
      totalDuration: { $sum: "$millis" }
    }
  },
  { $sort: { totalDuration: -1 } },
  { $limit: 10 }
]);

Bu şok edici bir şey ortaya çıkardı: saniyeler içinde process edilmesi gereken veriyi 12 dakikada işleyen aggregation pipeline’larımız vardı. Suçlu? MongoDB’nin built-in aggregation framework’ünü kullanmak yerine uygulama seviyesinde join’ler yapıyorduk.

Eksik Composite Index Sorunu

Yoğun alışveriş trafiğinde ürün arama sisteminin sürünmesi klasik bir durumdur. Kullanıcılar arama sonuçlarının yüklenmesi 8+ saniye sürdüğünde sepetlerini terk eder.

PostgreSQL logları kök nedeni gösterir:

-- Katil query (basitleştirilmiş)
SELECT p.*, c.name as category_name 
FROM products p 
JOIN categories c ON p.category_id = c.id 
WHERE p.active = true 
  AND p.category_id = $1 
  AND p.price BETWEEN $2 AND $3 
ORDER BY p.rating DESC, p.created_at DESC;

EXPLAIN planı her arama için 50 milyon ürün üzerinde sequential scan yaptığını gösterir. category_id ve price üzerinde ayrı index’ler var, ancak bu query’yi hızlandıracak composite index yok.

Kısa bir maintenance window sırasında eksik index eklenir:

CREATE INDEX CONCURRENTLY idx_products_category_price_rating 
ON products (category_id, price, rating DESC, created_at DESC) 
WHERE active = true;

Query süresi 8 saniyeden 150ms’ye düşer. Bu tek index değişikliği, yoğun alışveriş dönemlerinde kullanıcı deneyimini köklü biçimde iyileştirir.

Dikkat edilmesi gereken denge: bu index write sürelerini yaklaşık %15 artırır. Her ürün güncellemesi ek bir index’i maintain etmek zorunda kalır. Performans optimizasyonu her zaman okuma-yazma gereksinimlerini dengelemeyi içerir.

MongoDB Aggregation Pipeline Uyanışı

PostgreSQL sorunu giderilirken MongoDB analitiği hala yavaştı. Kullanıcı dashboard’u 30+ saniye yüklendiğinde kullanıcılar browser tab’ını kapatmaya başlar.

Sorun veriyi düşünme biçimindedir. Tipik hatalı Node.js yaklaşımı:

// Yanlış yol - uygulama seviyesinde join'ler
const users = await User.find({ active: true });
const orders = await Order.find({ userId: { $in: userIds } });
const analytics = users.map(user => {
  const userOrders = orders.filter(o => o.userId === user._id);
  return {
    userId: user._id,
    totalRevenue: userOrders.reduce((sum, o) => sum + o.total, 0),
    orderCount: userOrders.length,
    avgOrderValue: userOrders.length > 0 ? totalRevenue / userOrders.length : 0
  };
});

Bu potansiyel olarak milyonlarca dökümanı memory’ye yüklemeyi ve JavaScript’te işlemeyi gerektiriyordu. Yavaş olması şaşırtıcı değildi.

MongoDB aggregation pipeline doğru kullanıldığında çözüm açıktır:

// Doğru yol - veritabanı seviyesinde aggregation
const analytics = await User.aggregate([
  { $match: { active: true } },
  { $lookup: {
      from: "orders",
      localField: "_id",
      foreignField: "userId",
      as: "orders"
    }
  },
  { $project: {
      userId: "$_id",
      totalRevenue: { $sum: "$orders.total" },
      orderCount: { $size: "$orders" },
      avgOrderValue: { 
        $cond: [
          { $gt: [{ $size: "$orders" }, 0] },
          { $divide: [{ $sum: "$orders.total" }, { $size: "$orders" }] },
          0
        ]
      }
    }
  }
]);

İşleme süresi 12 dakikadan 3 saniyeye düştü. Kullanıcı dashboard’u artık real-time yükleniyor. Bazen en iyi optimizasyon sadece veritabanınızı tasarlandığı şekilde kullanmaktır.

Real-Time Performance Monitoring Kurma

Regresyonları kullanıcılara ulaşmadan yakalamak için kapsamlı bir monitoring sistemi kurulur:

interface DatabaseMetrics {
  postgresql: {
    activeConnections: number;
    queryDuration: PercentileMetrics;
    cacheHitRatio: number;
    indexUsage: IndexEfficiency[];
    lockWaitTimes: Duration[];
  };
  
  mongodb: {
    operationCounts: OperationType[];
    queryExecutionStats: ExecutionStats;
    indexEffectiveness: IndexMetrics[];
    shardingBalance: ShardDistribution;
  };
  
  infrastructure: {
    cpuUtilization: number;
    memoryUsage: MemoryMetrics;
    diskIOPS: IOMetrics;
    networkLatency: NetworkStats;
  };
}

// Özel query performance tracker
class QueryPerformanceTracker {
  private metrics = new Map<string, QueryMetrics>();
  
  async trackQuery(query: string, duration: number, database: 'postgres' | 'mongodb') {
    const querySignature = this.normalizeQuery(query);
    const existing = this.metrics.get(querySignature) || {
      count: 0,
      totalDuration: 0,
      maxDuration: 0,
      database
    };
    
    existing.count++;
    existing.totalDuration += duration;
    existing.maxDuration = Math.max(existing.maxDuration, duration);
    
    this.metrics.set(querySignature, existing);
    
    // Regresyonda alert
    if (duration > existing.maxDuration * 1.5) {
      await this.alertPerformanceRegression(querySignature, duration);
    }
  }
}

Bu sistem artık performance regresyonlarını otomatik yakalıyor. Developer bir pull request’te yanlışlıkla indexsiz query eklediğinde, production’a ulaşmadan önce bundan haberimiz oluyor.

Veritabanı Optimizasyonunun Ekonomisi

Gerçek maliyetleri açıklayalım, çünkü optimizasyon sadece teknik egzersizden ziyade iş case’i haline burada dönüşüyor.

Optimizasyon Öncesi (Aylık AWS Maliyetleri):

  • RDS PostgreSQL (db.r5.4xlarge): $1,200/ay
  • Read replica’lar (3x db.r5.xlarge): $900/ay
  • MongoDB Atlas (M60): $2,500/ay
  • Uygulama sunucuları (yavaş query’ler için extra kapasite): $800/ay
  • Toplam: $5,400/ay ($64,800 yıllık)

Optimizasyon Sonrası (Aylık AWS Maliyetleri):

  • RDS PostgreSQL (db.r5.2xlarge): $600/ay
  • Read replica’lar (2x db.r5.large): $400/ay
  • MongoDB Atlas (M30): $1,000/ay
  • Uygulama sunucuları (azaltılmış kapasite): $400/ay
  • Toplam: $2,400/ay ($28,800 yıllık)

Yıllık Tasarruf: Altyapıda $36,000 + önlenmiş scaling maliyetlerinde $64,000 = $100K

Ama gerçek değer sadece maliyet tasarrufu değildi. Kullanıcı deneyimi dramatik şekilde gelişti:

  • Ürün arama: 8 saniye → 150ms (%98 iyileşme)
  • Analytics dashboard: 30+ saniye → real-time güncellemeler
  • Checkout flow: Yoğun trafik sırasında artık timeout yok
  • Müşteri memnuniyeti: Conversion rate’lerde %23 artış

PostgreSQL vs MongoDB: Doğru Aracı Seçme

Her iki sistemin optimizasyonu, hangisini ne zaman kullanacağınıza dair net bir tablo çiziyor:

PostgreSQL kullan:

  • ACID uyumluluğuna ihtiyacın varsa (finansal işlemler, envanter yönetimi)
  • Karmaşık JOIN operasyonları query’lerinde yaygınsa
  • Öngörülebilir performans karakteristiği istiyorsan
  • Takımın NoSQL konseptlerinden çok SQL’e aşinaysa
  • Data tutarlılığı eventual consistency’den daha önemliyse

MongoDB kullan:

  • Built-in horizontal scaling’e ihtiyacın varsa
  • Data modelin sık değişiyorsa (startup pivot’ları, hızlı iterasyon)
  • Büyük veri setlerinde karmaşık aggregation’lar yapıyorsan
  • Döküman tabanlı query’ler uygulama objelerinle match ediyorsa
  • Kabul edilebilir write latency ile mükemmel read performansına ihtiyacın varsa

Temel görüş: Veritabanını hype’a veya trend’e göre seçme. Spesifik erişim pattern’larına ve tutarlılık gereksinimlerine göre seç.

Gerçekten İşe Yarayan Advanced Profiling Araçları

Veritabanı profiling araçlarını değerlendirmek, hangilerinin gerçek değer sunduğunu gösterir:

PostgreSQL Araçları

Log Analysis için pgBadger:

# Kapsamlı performans raporları oluştur
pgbadger -j 4 -f stderr /var/log/postgresql/postgresql-*.log \
  --prefix '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h' \
  -o /var/www/html/pgbadger.html

pgBadger PostgreSQL loglarını aksiyon alınabilir raporlara dönüştürür; ayrı ayrı hızlı ancak frekans nedeniyle toplam sürenin büyük bölümünü tüketen query’leri tespit eder.

Percona Monitoring and Management (PMM): PMM historical trending ile real-time query analytics sağlar. Index recommendation engine’i manuel analiz süresini önemli ölçüde kısaltır.

MongoDB Araçları

MongoDB Compass: Compass’taki görsel explain planları inefficient query’leri spot etmeyi kolaylaştırıyor. Aggregation pipeline’ının shard’lar arasında tam olarak nasıl execute edildiğini görebilmek paha biçilmez.

Özel Profiling Script’leri:

// Otomatik yavaş query detection
function analyzeSlowQueries() {
  return db.system.profile.aggregate([
    { $match: { millis: { $gt: 100 } } },
    { $group: {
        _id: {
          collection: "$ns",
          operation: "$op"
        },
        count: { $sum: 1 },
        avgDuration: { $avg: "$millis" },
        maxDuration: { $max: "$millis" }
      }
    },
    { $sort: { avgDuration: -1 } }
  ]);
}

Bu script günlük çalışır ve en yavaş operasyonları raporlar; aksi halde fark edilmeyecek performance regresyonlarını yakalamak için etkili bir yöntem.

Yaptığım Yaygın Hatalar (Ve Nasıl Kaçınılır)

“Daha Fazla Index Ekle” Tuzağı

Index’leri sihir olarak görmek yaygın bir başlangıç hatasıdır: yavaş query varsa index ekle, hala yavaşsa bir tane daha ekle. Çok geçmeden 12+ index’li tablolar ortaya çıkar ve write performansı %60 düşer.

Ders: Her index read’leri hızlandırır ama write’ları yavaşlatır. Indexing stratejini read/write oranına göre tasarla. High-write tablolar için index konusunda çok seçici ol.

Production Data Sürprizi

Her optimizasyon development ortamında 10.000 test kaydıyla mükemmel çalışabilir. Production’da 50 milyon kayıt ve tamamen farklı data dağıtım pattern’ları bulunur.

Development’te 10ms süren bir query production’da 30 saniye sürüyordu çünkü data’da index’lerimizi etkisiz kılan skewed distribution’lar vardı.

Optimizasyonları her zaman production ölçeğindeki data ile test et, ya da en azından benzer dağıtım karakteristiklerine sahip data ile.

Yanlış Metrikler Odağı

Aylarca yanlış query’ler optimize edilebilir: kullanıcı odaklı operasyonlar yerine veritabanı merkezli metrikler ölçüldüğünde, user-facing arama query’leri yavaş kalırken gecelik batch process’ler optimize edilmiş olur.

Önce kullanıcı deneyimini doğrudan etkileyen operasyonlara odaklan. İç raporlama yavaş olabilir - kullanıcı odaklı operasyonlar olamaz.

Önerilen Yaklaşımlar

Business Impact Measurement ile Başla En yavaş query’leri optimize etmek yerine, önce en yüksek business impact’li query’leri hedeflemek gerekir. Günde 10.000 kez hit alan query’de 1 saniyelik iyileşme, günde bir kez hit alan query’de 10 saniyelik iyileşmeden daha değerli.

Otomatik Performance Testing Implement Et CI/CD pipeline’ına otomatik performance regression testing kur. Performance sorunlarını müşteri şikayetlerinden sonra değil, code review sırasında yakala.

Takım Eğitimine Erken Yatırım Yap Optimizasyona başlamadan önce tüm geliştirme takımını veritabanı performans temellerinde eğit. Query yazan her developer EXPLAIN planları ve temel indexing stratejilerini anlamalı.

10x Data Growth için Plan Yap Optimizasyon stratejilerini mevcut data hacminin 10 katında çalışacak şekilde tasarla. Büyüme planlamazsan bugünün hızlı query’si yarının timeout’u olur.

Teknik Liderler için Ana Çıkarımlar

Farklı ortamlardaki veritabanı optimizasyon uygulamalarından çıkan tekrarlayan içgörüler:

Veritabanı Yöneticileri için:

  • Sistematik monitoring ve profiling sezgi tabanlı optimizasyonu her zaman yener
  • Index stratejisi read performansı ile write overhead’i dengelemeli
  • Veritabanı teknoloji seçimi spesifik use case pattern’larına match etmeli
  • Otomatik alerting küçük sorunların major incident olmasını önler

Geliştirme Takımları için:

  • Query optimizasyonu sürekli öğrenme ve pratik gerektiren bir beceri
  • Uygulama tasarım pattern’larının veritabanı performansına büyük etkisi var
  • Production ölçeğindeki data ile test anlamlı optimizasyon için şart
  • Performance regression prevention performance incident response’dan ucuz

Engineering Leadership için:

  • Veritabanı performans optimizasyonu ölçülebilir business ROI sağlar
  • Takım eğitimi ve tooling yatırımı tüm projelerde dividendlar ödüyor
  • Optimizasyon ile altyapı maliyet azaltma kapasite eklmeden daha iyi scale ediyor
  • Performance sorunları zamanla compound ediyor - erken yatırım büyük sorunları önlüyor

İleriye Giden Yol

Veritabanı optimizasyonu bir kerelik proje değil, süregelen bir disiplin. Sistematik profiling, monitoring ve optimizasyon pratiklerini geliştirme workflow’una entegre etmek, sayısız performance sorununu önler ve ölçeklendikçe öngörülebilir maliyetleri korur.

Burada paylaştığım araçlar ve teknikler sihirli mermi değil. Veritabanı performansını anlama ve iyileştirmenin sistematik yaklaşımının parçaları. Gerçek sihir, iyi tooling’i spesifik use case’lerinizin ve erişim pattern’larınızın derin anlaşılmasıyla birleştirdiğinde oluyor.

Benzer veritabanı performans challengeları yaşıyorsan, ölçümle başla. Ölçemediğin şeyi optimize edemezsin. Önce kapsamlı monitoring kur, sonra o veriyi optimizasyon çabalarını yönlendirmek için kullan.

Ve unutma: amaç dünyadaki en hızlı veritabanına sahip olmak değil. Amaç bütçeni patlatmadan business hedeflerini destekleyecek kadar iyi performans veren bir veritabanına sahip olmak.

Kullanıcıların sub-millisecond query süreleri için teşekkür etmeyecek, ama arama 8 saniye sürerse ürününü terk edecekler. Spesifik durumun için doğru dengeyi bul ve büyürken onu maintain edecek süreçleri kur.

Kaynaklar

İlgili yazılar

LangChain Production'da: Çalışan Patternler ve İşe Yaramayan Anti-Patternler

LangChain uygulamalarını production'a taşırken öğrendiklerim. Başarısızlığa yol açan anti-patternler, başarıyı sağlayan patternler, çalışan kod örnekleri ve maliyet optimizasyon stratejileri.

langchainllmproduction+5
AWS CDK Link Shortener Bölüm 4: Production Deployment ve Optimizasyon

Multi-environment deployment stratejileri, ölçekte performans optimizasyonu, ve maliyet yönetimi. Production deneyimleri ve öğrenilen dersler ile doğru monitoring ve incident response pattern'ları.

aws-cdklambdadynamodb+6
Veritabanı Seçim Rehberi: Klasikten Edge'e - Kapsamlı Mühendislik Perspektifi

Projeniz için doğru veritabanını seçmek için kapsamlı rehber - SQL, NoSQL, NewSQL ve edge çözümlerini gerçek dünya implementasyon hikayeleri ve performans ölçümleri ile kapsıyor.

databasepostgresqlmysql+8
React Native Expo ile Sentry Entegrasyonu: Pratik Hızlı Rehber

React Native Expo uygulamasına Sentry hata izleme entegrasyonu için adım adım rehber. SDK başlatma, Expo Router enstrümantasyonu, session replay, EAS Build ve EAS Update için source map yükleme ve sık karşılaşılan sorunları kapsar.

react-nativeexpomonitoring+2
DynamoDB Rate Limiting: Single Table Design'da Ölçekte Stratejiler

Single Table Design uygulamalarında DynamoDB throttling'i önleme ve yönetme stratejileri. Partition key tasarımı, write sharding, kapasite modları, DAX caching, retry pattern'leri ve yüksek throughput sistemler için CloudWatch monitoring konularını kapsar.

dynamodbawsrate-limiting+5