SQL Sorgularınız Neden Yavaş? MSSQL ve PostgreSQL'de Performans Teşhisi
"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 Scan | Index yok, tüm tablo okunuyor |
Clustered Index Scan | Index var ama tamamı okunuyor (range problemi) |
Key Lookup | Index 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 ok | Beklenenin ç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:
- ✅ Sorgu metnini al (
pg_stat_statementsveya Query Store) - ✅
EXPLAIN ANALYZE/ Actual Execution Plan çalıştır - ✅
Seq Scan/Table Scanvar mı? - ✅ Missing index önerisi var mı?
- ✅ Satır tahmini gerçekten çok farklı mı? → istatistik güncelle
- ✅ Aktif lock/blocking var mı?
- ✅ 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.