Veritabanı optimizasyonu çalışmalarında yanlış kardinalite tahminlerini ölçmeyi, PostgreSQL ve SQL Server istatistiklerini düzeltmeyi ve plan değişimini önce-sonra verisiyle doğrulamayı ele alır.
Veritabanı Optimizasyonu: Kardinalite Tahmini ve İstatistik Hataları
Veritabanı optimizasyonu için tahmin-gerçek satır farkını ölçmek
Kötü planın kök nedeni çoğu zaman sorgunun kendisi değil, optimizer'ın ara operatörlerde beklediği satır sayısının gerçek sayıdan sapmasıdır. PostgreSQL'de üretime yakın veri dağılımıyla önce `EXPLAIN (ANALYZE, BUFFERS, SETTINGS, FORMAT JSON)` çalıştırın; her düğümdeki `Plan Rows` ve `Actual Rows` değerlerini karşılaştırın. Örneğin iç içe döngüde dış tarafta 40 satır yerine 400.000 satır gelirse, indeks seek maliyeti 400.000 kez ödenir ve hash join daha doğru seçenek olabilirdi.
EXPLAIN (ANALYZE, BUFFERS, SETTINGS)
SELECT o.id, o.created_at
FROM orders o
WHERE o.country_code = 'TR'
AND o.status = 'PAID'
AND o.created_at > now() - interval '30 days';Önce-sonra karşılaştırmasını yalnızca toplam süreyle yapmayın. Aynı parametre setinde `actual rows / estimated rows` oranını, `shared hit/read` bloklarını ve geçici dosya kullanımını kaydedin. PostgreSQL tarafında en az 10 kat sapma görülen düğümleri `pg_stat_statements` ile çağrı sayısına göre sıralamak, tekil ama önemsiz bir sorgu yerine toplam CPU ve I/O tüketen sorguya odaklanmayı sağlar. Bu yaklaşım, bir veritabanı eğitimi veya sql eğitimi kapsamında ezberlenen "indeks ekle" refleksinden daha güvenilirdir; önce optimizer'ın hangi varsayımının yanlış olduğunu gösterir.
SELECT queryid,
calls,
round(mean_exec_time::numeric, 2) AS mean_ms,
shared_blks_read,
temp_blks_written,
left(query, 180) AS sample_query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;PostgreSQL'de bağımlı kolonlar için istatistik düzeltmesi
Tek kolon histogramları, `country_code='TR'` ile `currency='TRY'` gibi ilişkili filtreleri bağımsız kabul eder. Gerçekte Türkiye siparişlerinin çoğu TRY ise optimizer iki seçiciliği çarpar ve sonuç kümesini gereğinden küçük tahmin eder. PostgreSQL'de bu ilişkiyi indeksle değil, `CREATE STATISTICS` ile modele ekleyin; ardından `ANALYZE` zorunludur, aksi halde katalogda nesne bulunur fakat planlayıcı yeni veriyi kullanmaz.
CREATE STATISTICS orders_country_currency_dep
(dependencies, mcv, ndistinct)
ON country_code, currency
FROM orders;
ANALYZE orders;
SELECT stxname, stxkind
FROM pg_statistic_ext
WHERE stxrelid = 'orders'::regclass;Bu değişiklikten önce ve sonra aynı `EXPLAIN (ANALYZE, BUFFERS)` çıktısında özellikle filtre düğümünün tahminini karşılaştırın. `dependencies` fonksiyonel veya kuvvetli kolon ilişkilerini, `mcv` sık görülen kombinasyonları, `ndistinct` ise `GROUP BY country_code, currency` gibi sorgulardaki benzersiz kombinasyon sayısını iyileştirir. İncelik şudur: üç kolonlu bir filtre için yalnızca iki kolonlu istatistik oluşturmak her zaman yeterli değildir; sorgu yükünde birlikte geçen kolon kümelerini `pg_stat_statements` içinden çıkarın. Çok geniş kombinasyonlar için de istatistik hedefini kontrollü yükseltin; katalog toplama maliyetini ölçmeden tablo genelinde rastgele `SET STATISTICS 10000` vermeyin.
ALTER TABLE orders
ALTER COLUMN status SET STATISTICS 500;
ANALYZE VERBOSE orders (status);
SELECT attname, n_distinct, most_common_vals
FROM pg_stats
WHERE tablename = 'orders'
AND attname = 'status';SQL Server eğitiminde çok kolonlu istatistik ve plan doğrulaması
SQL Server'ın otomatik oluşturduğu istatistikler çoğunlukla tek kolonludur; bileşik indeksin istatistiği ise yalnızca sol-baş kolon üzerinde histogram taşır, sonraki kolonlar için density bilgisine dayanır. `TenantId` ve `IsDeleted` birlikte filtreleniyorsa, mevcut bileşik indeks bu korelasyonu yeterince temsil etmeyebilir. Bu nedenle sql server eğitimi pratiğinde önce gerçek yürütme planını açın, ardından tahmin-gerçek farkını XML planda `EstimateRows` ve `ActualRows` alanlarından doğrulayın; sonra hedefli çok kolonlu veya filtreli istatistik ekleyin.
CREATE STATISTICS ST_Invoice_Tenant_Deleted
ON dbo.Invoice (TenantId, IsDeleted)
WITH FULLSCAN;
GO
SELECT s.name, sp.last_updated, sp.rows, sp.rows_sampled
FROM sys.stats AS s
CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp
WHERE s.object_id = OBJECT_ID(N'dbo.Invoice');Sık yapılan hata, istatistik yenilendikten sonra plan önbelleğinde eski planın kalmasına rağmen değişikliğin işe yaramadığını varsaymaktır. Test ortamında aynı parametrelerle `OPTION (RECOMPILE)` kullanarak yeni istatistiğin etkisini izole edin; üretimde ise körlemesine `DBCC FREEPROCCACHE` çalıştırmak tüm iş yükünü derleme fırtınasına sokabilir. Query Store etkinse plan kimliği, ortalama süre, mantıksal okuma ve bellek bağışı (`GrantedMemoryKb`) metriklerini dağıtımdan önce ve sonra karşılaştırın.
SELECT TOP (20)
q.query_id,
p.plan_id,
rs.avg_duration / 1000.0 AS avg_duration_ms,
rs.avg_logical_io_reads,
rs.avg_query_max_used_memory
FROM sys.query_store_runtime_stats AS rs
JOIN sys.query_store_plan AS p ON p.plan_id = rs.plan_id
JOIN sys.query_store_query AS q ON q.query_id = p.query_id
ORDER BY rs.avg_duration DESC;Database indexleme ile istatistiği birbirinden ayırmak
Database indexleme, satıra erişim maliyetini düşürür; istatistik ise optimizer'ın kaç satıra erişeceğini tahmin etmesini sağlar. Yanlış tahminli bir sorguya hemen indeks eklemek, seçiciliği düşük bir indeks üretip yazma maliyetini artırabilir. PostgreSQL'de bir aday indeksi önce `hypopg` ile fiziksel olarak oluşturmadan plan üzerinde deneyin. Hipotetik indeks planı değiştirmiyor veya tahmini satır sayısı h芒l芒 yanlış kalıyorsa sorun erişim yolu değil, veri dağılımı ya da kolon korelasyonudur.
CREATE EXTENSION IF NOT EXISTS hypopg;
SELECT * FROM hypopg_create_index(
'CREATE INDEX ON orders (country_code, status, created_at DESC)'
);
EXPLAIN SELECT id
FROM orders
WHERE country_code = 'TR'
AND status = 'PAID'
ORDER BY created_at DESC
LIMIT 100;
SELECT * FROM hypopg_reset();İndeks kararını `EXPLAIN` ile bitirmeyin: yazma yoğun tablolarda PostgreSQL `pg_stat_user_indexes.idx_scan` sayısını, indeks boyutunu ve `pgstattuple` ile boş alan oranını izleyin. SQL Server'da benzer karar için `sys.dm_db_index_usage_stats` içindeki seek/scan ile user_updates değerlerini birlikte okuyun. Sıfır seek görülen bir indeksin otomatik silineceği sonucuna acele etmeyin; aylık rapor veya failover sonrası DMV sıfırlanması bu metriği yanıltabilir.
NoSQL eğitimi: MongoDB cardinality sınırlarını okumak
Nosql eğitimi kapsamında MongoDB için de aynı teşhis geçerlidir, fakat klasik ilişkisel histogram ayrıntıları yerine `explain('executionStats')` çıktısındaki `totalKeysExamined`, `totalDocsExamined` ve `nReturned` oranlarına bakılır. `nReturned=100` iken milyonlarca anahtar taranıyorsa bileşik indeks sırası sorgunun eşitlik, sıralama ve aralık koşullarına uymuyor olabilir. Örnekte `tenantId` eşitlik filtresi önce, `createdAt` aralık ve sıralama alanı sonra konumlanır.
db.events.createIndex({ tenantId: 1, createdAt: -1, eventType: 1 })
db.events.find({
tenantId: "t-42",
createdAt: { $gte: ISODate("2026-08-01T00:00:00Z") },
eventType: "payment"
}).sort({ createdAt: -1 }).limit(100).explain("executionStats")Buradaki edge case, düşük kardinaliteli `eventType` alanını indeksin başına koymaktır: birkaç farklı değer için geniş indeks aralıkları oluşur ve sonraki `tenantId` seçiciliği etkisiz kalabilir. Ancak tüm sorgular `eventType` ile başlıyor ve koleksiyonun büyük bölümü tek bir tenant yerine global sorgulanıyorsa sıra değişebilir; bunu varsayımla değil, temsil卯 parametrelerle `keysExamined/nReturned` oranını önce-sonra kaydederek test edin. Bu ölçüm, ilişkisel veritabanı performans ayarlama yaklaşımındaki tahmin-gerçek karşılaştırmasının doküman veritabanındaki karşılığıdır.
İlgili Eğitim
Sık Sorulan Sorular
Veritabanı optimizasyonu sırasında estimated rows ile actual rows farkı kaç olmalıdır?
Tek bir evrensel eşik yoktur; fakat 10x ve üzeri sapmayı inceleme kuyruğuna alın. Özellikle nested loop dış girdisinde 100x sapma, iç taraftaki seek veya lookup maliyetini çarpar. PostgreSQL'de `EXPLAIN (ANALYZE, BUFFERS)`, SQL Server'da actual execution plan ile her operatörü karşılaştırın.
SQL Server eğitiminde çok kolonlu istatistik ne zaman oluşturulmalı?
Aynı WHERE veya JOIN koşullarında birlikte geçen kolonların seçiciliği bağımsız değilse ve actual plan tahmini ciddi biçimde kaçırıyorsa oluşturun. `CREATE STATISTICS ... ON dbo.TableName (ColA, ColB) WITH FULLSCAN` sonrasında Query Store'dan aynı query_id için süre, logical reads ve bellek kullanımını önce-sonra kıyaslayın.
Database indexleme mi yoksa PostgreSQL extended statistics mi önce uygulanmalı?
Önce planın yanlış satır tahmini yapıp yapmadığını doğrulayın. Tahmin doğru fakat çok fazla blok okunuyorsa indeks adayı güçlüdür; tahmin yanlışsa `CREATE STATISTICS (... dependencies, mcv ...)` ve `ANALYZE` daha doğrudan çözümdür. Hipotetik indeksi `hypopg` ile deneyerek gereksiz fiziksel indeks oluşturmadan karar verin.
NoSQL eğitimi için MongoDB sorgu planında hangi metrikler izlenmeli?
`executionStats` altında `nReturned`, `totalKeysExamined`, `totalDocsExamined` ve `executionTimeMillis` değerlerini kaydedin. İyi bir hedef, dönen belge sayısına göre anahtar ve belge inceleme sayısını düşük tutmaktır; oran büyüyorsa bileşik indeks alan sırasını sorgunun eşitlik-aralık-sıralama yapısına göre yeniden test edin.
AI / LLM Discovery
Bu makale Opendart Akademi Veritabanı eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.


