PostgreSQL'u yıllardır kullanıyorum ve hala zor yoldan öğrenmeye devam ediyorum — genellikle gece 2'de, 50 milisaniye süren bir üretim sorgusu aniden 30 saniye sürmeye başladığında. Bildiğim her performans dersi gerçek bir durumdan geldi ve en büyük farkı yaratanları paylaşmak istiyorum.
Bunlar bir ders kitabından teorik ipuçları değil. Bunlar üretimde beni gerçekten kurtaran şeyler.
1. EXPLAIN ANALYZE En İyi Arkadaşınız
Herhangi bir şeyi optimize etmeden önce gerçekte ne olduğunu bilmeniz gerekir. Asla tahmin etmeyin. Ölçün.
EXPLAIN ANALYZE
SELECT p.id, p.title, p.slug, p.excerpt, p.created_at
FROM posts p
WHERE p.category_id = 3 AND p.published = true
ORDER BY p.created_at DESC
LIMIT 20;
Çıktı size her şeyi söyler: PostgreSQL'un hangi indeksleri kullandığını, kaç satır tahmin ettiğini vs gerçekte taradığını, her işlemde harcanan zamanı.
Dikkat edilecek şeyler:
- Büyük tabloda Seq Scan — genellikle eksik indeks demektir
- Tahmini vs gerçek satırlar — büyük farklar güncel olmayan istatistikler demektir
- Yüksek satır sayılı Nested Loop — muhtemel N+1 veya eksik join indeksi
100ms'den uzun süren her sorguda EXPLAIN ANALYZE çalıştırıyorum. Alışkanlık haline getirin.
2. Gerçekten Sorguladığınızı İndeksleyin
Açık görünüyor ama milyonlarca satırı olan ve birincil anahtar dışında indeksi olmayan üretim veritabanları gördüm. PostgreSQL yabancı anahtarlarda otomatik indeks oluşturmaz.
-- Temel indeks CREATE INDEX idx_posts_category_id ON posts(category_id);
-- Yaygın sorgu kalıbı için bileşik indeks CREATE INDEX idx_posts_category_published ON posts(category_id, created_at DESC) WHERE published = true;
İkinci indeks kısmi indekstir — sadece published = true olan satırları içerir. Taslak postları atladığı için daha küçük ve hızlıdır.
Bileşik indeks sütun sırası önemlidir. Eşitlik koşullarını önce (category_id = 3), aralık/sıralama sütunlarını sona (created_at DESC) koyun.
3. SELECT * Kullanmayı Bırakın
SELECT * geliştirme sırasında rahat ama üretimde gerçek maliyetleri var:
-- Liste sayfasında ihtiyacınız olmayan 10KB HTML içerikli -- TEXT sütunu dahil HER ŞEYİ çeker SELECT * FROM posts WHERE published = true;
-- Sadece gerçekten gereken sütunları çeker SELECT id, title, slug, excerpt, image_url, created_at FROM posts WHERE published = true;
Fark dramatik olabilir. content sütununuz ortalama 10KB ise ve 20 satır çekiyorsanız, nedensiz yere 200KB veri transfer ediyorsunuz.
4. N+1 Sorgu Problemi
ORM kullanan uygulamalardaki en yaygın performans katilidir:
List<Post> posts = postRepository.findAll(); // 1 sorgu
for (Post post : posts) {
post.getCategory().getName(); // N sorgu!
post.getAuthor().getUsername(); // N daha sorgu!
}
Tembel yüklemeyle her post.getCategory() erişimi ayrı bir veritabanı sorgusu tetikler. 50 postunuz varsa, 1 yerine 101 veritabanı gidiş-dönüşü.
Düzeltme:
@EntityGraph(attributePaths = {"category", "author", "tags"})
List<Post> findByPublishedTrue();
101 yerine tek sorgu. Bu düzeltme çalıştığım neredeyse her projede en büyük performans iyileştirmesi oldu.
5. Bağlantı Havuzu Yapılandırması
Her veritabanı bağlantısının maliyeti vardır. HikariCP bağlantı havuzunu yönetir:
spring:
datasource:
hikari:
maximum-pool-size: 10
minimum-idle: 5
connection-timeout: 30000
Havuz boyutu doğru olmalı. Çok küçük: istekler bağlantı bekler. Çok büyük: veritabanını eş zamanlı bağlantılarla bunaltırsınız.
Sorgularınız hızlıysa 10 bağlantı şaşırtıcı miktarda trafiği kaldırabilir.
6. Toplu İşlemler Kullanın
Satırları tek tek eklemek satır başına bir veritabanı gidiş-dönüşü demektir.
-- Yavaş: 1000 ayrı INSERT INSERT INTO tags (name, slug) VALUES ('java', 'java');
-- Hızlı: tek çok satırlı INSERT INSERT INTO tags (name, slug) VALUES ('java', 'java'), ('python', 'python'), ('docker', 'docker');
JPA'da toplu işlemeyi etkinleştirin:
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 50
order_inserts: true
7. VACUUM ve Ölü Satırlar
PostgreSQL MVCC kullanır. Bir satırı güncellediğinizde veya sildiğinizde eski versiyon hemen kaldırılmaz — "ölü satır" olarak işaretlenir.
Zamanla ölü satırlar birikir: tablo diskte büyür, indeks taramaları yavaşlar.
Autovacuum PostgreSQL'un yerleşik temizlik sürecidir. Ama bazen geride kalır:
SELECT
relname AS tablo,
n_live_tup AS canli_satirlar,
n_dead_tup AS olu_satirlar,
ROUND(n_dead_tup::numeric / NULLIF(n_live_tup, 0) * 100, 1) AS olu_yuzde,
last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;
8. LIKE Yerine Full-Text Search
Arama için LIKE '%keyword%' kullanıyorsanız her arama sorgusunda tam tablo taraması yapıyorsunuz:
-- Yavaş: hiçbir indeks kullanamaz SELECT * FROM posts WHERE title ILIKE '%docker%';
-- Hızlı: tsvector sütununda GIN indeks kullanır SELECT * FROM posts WHERE search_vector @@ plainto_tsquery('simple', 'docker');
PostgreSQL'un yerleşik full-text search'ü şaşırtıcı derecede yeteneklidir. Çoğu uygulama için Elasticsearch gerekmez.
9. Uygun Veri Tipleri Kullanın
Veri tipleri hem depolama boyutunu hem sorgu performansını etkiler:
-- Kötü: tarihleri string olarak saklamak event_date VARCHAR(10) -- '2026-04-15'
-- İyi: uygun tarih tipi event_date DATE -- 4 byte, karşılaştırmalar doğru ve hızlı
10. Uzun Süren Sorguları İzleyin
SELECT pid, now() - query_start AS sure, query, state
FROM pg_stat_activity
WHERE (now() - query_start) > interval '5 seconds'
AND state != 'idle'
ORDER BY sure DESC;
log_min_duration_statement = 500 ayarlayarak 500ms'den uzun süren tüm sorguları loglamayı düşünün.
Veritabanı Performansının 80/20 Kuralı
Karşılaştığım performans sorunlarının çoğu üç kaynaktan gelir:
1. Eksik indeksler — WHERE ve JOIN sütunlarında 2. N+1 sorgular — tembel yüklenen ORM ilişkilerinden 3. Çok fazla veri çekmek — 3 sütuna ihtiyaç duyulduğunda SELECT *
Bu üçünü düzeltin ve tipik bir web uygulamasındaki PostgreSQL performans problemlerinin yüzde seksenini çözmüş olursunuz.
Geri kalan yüzde yirmi — tablo bölümleme, materialized view'lar, okuma replikaları — işlerin ilginçleştiği yerdir. Ama gerçek ölçeğe (milyonlarca satır, binlerce eş zamanlı kullanıcı) ulaşana kadar nadiren bunlara ihtiyacınız olur.
Temellerle başlayın. Her şeyde EXPLAIN ANALYZE çalıştırın. Önemli olan yerlere indeks ekleyin. N+1 sorgularınızı düzeltin. Sıkıcı temel bilgiler sizi şaşırtıcı derecede ileri götürür.

Yorumlar (