Blog
VeritabanıMSSQLPostgreSQLPerformans

SQL Sorgularınız Neden Yavaş? MSSQL ve PostgreSQL'de Performans Teşhisi

16 Haziran 2026 · 10 dk okuma

"Uygulama yavaşladı" şikayetleriyle gelen çağrıların büyük çoğunluğunun arkasında bir veritabanı sorunu yatar. Sunucu kaldırılmış, uygulama optimize edilmiş ama problem yerinde duruyor — çünkü asıl darboğaz, kimsenin bakmadığı SQL sorgularındadır.

Bu yazıda MSSQL (SQL Server) ve PostgreSQL'de yavaş sorguları nasıl tespit ettiğinizi ve ilk müdahaleyi nasıl yapacağınızı adım adım göstereceğiz.

Neden Yavaş? Önce Teşhis, Sonra Tedavi

En yaygın hata: sorunu tahmin etmek. "Index ekleyeyim düzelir" veya "sunucuyu güçlendirelim" kararları, gerçek problemi görmeden verilir ve çoğu zaman boşa gider.

Teşhis şu soruyu yanıtlamalıdır: Bu sorgu neden bu kadar kaynak harcıyor?

Olası sebepler:

  • Table scan: Index yokken tüm tablo okunuyor
  • Index var ama kullanılmıyor: Yanlış sütun sırası, fonksiyon içinde kullanım, tip uyumsuzluğu
  • Aşırı row döndürme: Gereksiz veri, eksik filtreleme
  • Join sırası: Küçük tablo büyüğe göre join edilmeli
  • Stale statistics: Sorgu planlayıcı eski istatistiklerle kötü karar alıyor
  • Lock/blocking: Başka bir işlem kaynağı tutuyor
  • N+1 sorunu: ORM her satır için ayrı sorgu atıyor

MSSQL (SQL Server) — Adım Adım Teşhis

1. En Yavaş Sorguları Bul

SQL Server 2016 ve sonrası için Query Store en iyi başlangıç noktasıdır. GUI yerine SQL ile doğrudan sorgulayalım:

-- Son 24 saatte en fazla CPU harcayan 10 sorgu
SELECT TOP 10
    qs.total_worker_time / qs.execution_count   AS avg_cpu_ms,
    qs.total_elapsed_time / qs.execution_count  AS avg_duration_ms,
    qs.execution_count,
    SUBSTRING(qt.text, (qs.statement_start_offset / 2) + 1,
        ((CASE qs.statement_end_offset
            WHEN -1 THEN DATALENGTH(qt.text)
            ELSE qs.statement_end_offset END
        - qs.statement_start_offset) / 2) + 1)  AS query_text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
ORDER BY avg_cpu_ms DESC;

Query Store etkinse daha temiz:

SELECT TOP 10
    qsq.query_id,
    qsrs.avg_duration / 1000.0          AS avg_ms,
    qsrs.count_executions,
    qsqt.query_sql_text
FROM sys.query_store_query_text qsqt
JOIN sys.query_store_query      qsq  ON qsqt.query_text_id = qsq.query_text_id
JOIN sys.query_store_plan       qsp  ON qsq.query_id       = qsp.query_id
JOIN sys.query_store_runtime_stats qsrs ON qsp.plan_id = qsrs.plan_id
ORDER BY qsrs.avg_duration DESC;

2. Execution Plan Oku

Tespit ettiğin sorguyu SSMS'te seçip Ctrl+M (Actual Execution Plan) ile çalıştır.

Kırmızı bayraklar:

Plan OperatörüNe anlama geliyor
Table ScanIndex yok, tüm tablo okunuyor
Clustered Index ScanIndex var ama tamamı okunuyor (range problemi)
Key LookupIndex eksik sütunlar için ana tabloya gidiliyor
Hash Match (büyük maliyet)Bellekte join yapılıyor, index join daha iyi olur
Kalın okBeklenenin çok üzerinde satır transferi

3. Missing Index Önerisi

SQL Server çoğu zaman kendi önerisini verir. Plan'ın üst kısmında sarı "Missing Index Details" uyarısına tıkla ya da:

SELECT TOP 10
    mid.statement                                       AS tablo,
    migs.avg_total_user_cost * migs.avg_user_impact
        * (migs.user_seeks + migs.user_scans)           AS etki_skoru,
    mid.equality_columns,
    mid.inequality_columns,
    mid.included_columns
FROM sys.dm_db_missing_index_details  mid
JOIN sys.dm_db_missing_index_groups   mig  ON mid.index_handle = mig.index_handle
JOIN sys.dm_db_missing_index_group_stats migs ON mig.index_group_handle = migs.group_handle
ORDER BY etki_skoru DESC;

Uyarı: Her öneriyi doğrudan uygulamayın. Her index yazma işlemlerini yavaşlatır. Fayda/maliyet analizi yapın.


PostgreSQL — Adım Adım Teşhis

1. En Yavaş Sorguları Bul

pg_stat_statements eklentisi (genellikle varsayılan yüklü) şart:

-- Etkin değilse:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- En yavaş 10 sorgu (ortalama süreye göre)
SELECT
    round(mean_exec_time::numeric, 2)  AS ort_ms,
    calls                              AS cagri_sayisi,
    round(total_exec_time::numeric, 2) AS toplam_ms,
    LEFT(query, 120)                   AS sorgu
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;

2. EXPLAIN ANALYZE ile Planı İncele

EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT *
FROM siparisler s
JOIN musteriler m ON s.musteri_id = m.id
WHERE s.tarih >= '2026-01-01'
  AND s.durum = 'bekliyor';

Dikkat edilecekler:

Seq Scan on siparisler  (cost=0.00..45230.00 rows=892341 ...)

Seq Scan gördüğünde Index Scan bekliyorsanız sorun var. rows=892341 gerçek satır sayısından çok yüksekse istatistikler güncel değil demektir.

ANALYZE siparisler;  -- İstatistikleri güncelle

3. Gerçek Zamanlı Aktif Sorgular

Şu an ne çalışıyor, ne blokluyor:

SELECT
    pid,
    now() - pg_stat_activity.query_start AS sure,
    query,
    state,
    wait_event_type,
    wait_event
FROM pg_stat_activity
WHERE state != 'idle'
  AND query_start < now() - INTERVAL '5 seconds'
ORDER BY sure DESC;

wait_event_type = 'Lock' görüyorsanız bir sorgu diğerini blokluyor demektir. pg_cancel_backend(pid) ile uzun bloklayan sorguyu sonlandırabilirsiniz.


Her İkisinde Geçerli: En Sık Gözden Kaçan Hatalar

Index var ama kullanılmıyor

-- YANLIŞ: fonksiyon index'i devre dışı bırakır
WHERE UPPER(email) = 'TEST@TEST.COM'

-- DOĞRU: veriyi normalize et veya functional index oluştur
WHERE email = 'test@test.com'
-- YANLIŞ: tip uyumsuzluğu (varchar kolona int karşılaştırma)
WHERE musteri_kodu = 12345

-- DOĞRU
WHERE musteri_kodu = '12345'

N+1: ORM'in Sessiz Katili

ORM kullanan uygulamalarda çok yaygın. 1 sorgu yerine N+1 sorgu atılır:

-- Kötü (1000 sipariş varsa 1001 sorgu)
SELECT * FROM siparisler;
-- Sonra her satır için:
SELECT * FROM musteriler WHERE id = ?;

-- İyi (1 sorgu + JOIN veya eager loading)
SELECT s.*, m.* FROM siparisler s
JOIN musteriler m ON s.musteri_id = m.id;

Hızlı Kontrol Listesi

Bir yavaş sorguyla karşılaştığınızda ilk 10 dakika:

  1. ✅ Sorgu metnini al (pg_stat_statements veya Query Store)
  2. EXPLAIN ANALYZE / Actual Execution Plan çalıştır
  3. Seq Scan / Table Scan var mı?
  4. ✅ Missing index önerisi var mı?
  5. ✅ Satır tahmini gerçekten çok farklı mı? → istatistik güncelle
  6. ✅ Aktif lock/blocking var mı?
  7. ✅ ORM N+1 mi üretiyor?

Yavaş sorgu analizi çoğu zaman tahmin değil, metodoloji işidir. Doğru araçlarla problemi gördüğünüzde çözüm genellikle birkaç dakika alır.

Veritabanı performans sorunlarınız için teknik görüşme talep edebilirsiniz — MSSQL ve PostgreSQL ortamlarında yerinde inceleme yapıyoruz.