Tüm Yazılar
KategoriBackend
Okuma Süresi
17 dk
Yayın Tarihi
2025-01-30
Kelime Sayısı
3.411kelime

Kahveni hazırla - bu içerikli bir makale!

Database Indexing: Yavaş Sorguları Hızlandırmanın Bilimi

Özet

Veritabanı index sorgu performansı rehberi: EXPLAIN ANALYZE nasıl okunur, composite index'te kolon sırası neden kritik ve pg_stat_statements ile yavaş sorgular nasıl avlanır.

  • Index, tabloyu tam taramak yerine ilgili satırlara doğrudan erişim sağlar; ama her INSERT/UPDATE işleminde güncellenmesi gerektiği için yazma maliyeti ekler.
  • EXPLAIN ANALYZE çıktısında Seq Scan yerine Index Scan görmek tek başına yeterli değildir — actual time ve rows değerlerini planlayıcının tahminleriyle karşılaştırmak gerekir.
  • Composite (çok kolonlu) index'ler her kolon alt kümesiyle kullanılabilir ama en verimli kullanım soldan başlayan kolonlar filtrededir; kolon sırası sorgu desenine göre seçilmelidir.
  • pg_stat_statements modülü hangi sorgunun toplam zamanı en çok tükettiğini gösterir ve index ekleme kararlarını tahmine değil ölçüme dayandırır.
Database Indexing: Yavaş Sorguları Hızlandırmanın Bilimi

Aynı tabloya karşı çalışan iki sorgu neden bu kadar farklı sürede biter — biri gözle görülür şekilde yavaş, diğeri neredeyse anında döner? Cevap çoğu zaman tek bir kelimedir: index. Veritabanı index sorgu performansı konusu, doğru kolona doğru sırada index koymaktan EXPLAIN ANALYZE çıktısını doğru okumaya kadar uzanan pratik bir disiplindir ve bu rehberde bunu adım adım, PostgreSQL resmi dokümantasyonuna dayanarak anlatıyorum.

💡 Pro Tip: Yeni bir index eklemeden önce her zaman EXPLAIN (ANALYZE, BUFFERS) ile mevcut durumu ölç — index eklemek bazen sorunu çözmez, sadece yazma maliyetini artırır.

İçindekiler

Index gerçekte neyi hızlandırır, neyi yavaşlatır

PostgreSQL'in kendi tanımı net: "Indexes are a common way to enhance database performance. An index allows the database server to find and retrieve specific rows much faster than it could do without an index. But indexes also add overhead to the database system as a whole, so they should be used sensibly." Yani index tek yönlü bir kazanç değil, bir değiş tokuş.

Index olmadan planlayıcının elinde tek bir seçenek vardır: tablonun tamamını baştan sona okumak (sequential scan). Az satırlı küçük tablolarda bu zaten en hızlı yoldur — disk sayfası az, ek yapı gezmeye değmez. Ama tablo büyüdükçe ve sorgu satırların küçük bir yüzdesini hedeflediğinde, planlayıcı index üzerinden doğrudan ilgili sayfalara atlamayı tercih eder.

Buradaki kritik nokta şu: index'in var olması onun kullanılacağı anlamına gelmez. PostgreSQL'in planlayıcısı maliyet tahminine göre karar verir; sorgu tablonun büyük bir kısmını (örneğin yüzde birkaçından fazlasını) döndürüyorsa, index üzerinden gitmek yerine seq scan daha ucuz olabilir. Bu yüzden "index ekledim ama kullanılmıyor" şikâyetlerinin çoğu aslında planlayıcının doğru kararı olur.

Ne zaman index koymaya değer

  • Seçici WHERE/JOIN/ORDER BY kolonları: sık kullanılan, seçiciliği yüksek (az sayıda satırı işaret eden) kolonlar.
  • Foreign key kolonları: JOIN'lerde ve silme işlemlerinde ilgili satırları bulmak için sürekli taranır.
  • Raporlama filtre kolonları: büyük tablolarda sık çalışan raporlama sorgularının filtre kolonları.
  • Unique kısıtlama gereken kolonlar: zaten örtük olarak index oluşturur.

Ne zaman index koymamak daha mantıklı

  • Küçük tablolar: seq scan zaten hızlı, index bakımı net kayıptır.
  • Yazma-ağırlıklı kolonlar: sık yazılan ama nadiren filtrelenen kolonlar — her yazımda index de güncellenir.
  • Düşük seçicilikli filtreler: tablo satırlarının büyük yüzdesini döndüren filtreler — planlayıcı zaten seq scan seçer.
sql
1-- Basit ama tipik bir örnek: siparişleri müşteri id'sine göre filtreleyen bir sorgu
2CREATE INDEX orders_customer_id_idx ON orders (customer_id);
3 
4-- Bu index, aşağıdaki gibi bir sorguda devreye girer
5SELECT id, total_amount, created_at
6FROM orders
7WHERE customer_id = 4821
8ORDER BY created_at DESC;

Bu noktada elle "index koydum, bitti" demek yetmez. Bir sonraki adım, bu index'in gerçekten kullanılıp kullanılmadığını ve ne kadar fark yarattığını ölçmek — ki bunun aracı EXPLAIN ANALYZE.

EXPLAIN ANALYZE okumayı öğrenmek (seq scan vs index scan)

PostgreSQL dokümantasyonunun tenk1 örneği, bu konuyu anlatmanın en net yolu. Aynı tablo üzerinde, sorgunun seçiciliğine göre planlayıcı iki farklı taktik seçer:

sql
1-- Geniş bir aralık: planlayıcı Seq Scan'i tercih edebilir
2EXPLAIN ANALYZE SELECT * FROM tenk1 WHERE ten < 7;
3 
4-- Gerçek çıktı (PostgreSQL resmi dokümantasyonundan):
5-- Seq Scan on tenk1 (cost=0.00..470.00 rows=7000 width=244)
6-- (actual time=0.030..1.995 rows=7000.00 loops=1)
7-- Filter: (ten < 7)
8 
9-- Tek bir satırı hedefleyen dar bir eşitlik: Index Scan devreye girer
10EXPLAIN SELECT * FROM tenk1 WHERE unique1 = 42;
11 
12-- Gerçek çıktı (ANALYZE'sız, PostgreSQL resmi dokümantasyonundan):
13-- Index Scan using tenk1_unique1 on tenk1 (cost=0.29..8.30 rows=1 width=244)
14-- Index Cond: (unique1 = 42)
15-- (Burada actual time yok çünkü ANALYZE kullanılmadı, yalnızca planlayıcının tahmini maliyeti gösteriliyor.)

Bu satırları okurken bakılması gereken beş alan var:

Alan
Ne anlatır
cost=0.00..470.00
Planlayıcının tahmini başlangıç ve toplam maliyeti (disk sayfası birimi, gerçek zaman değil)
actual time=0.030..1.995
Gerçek ölçülen başlangıç ve bitiş süresi (milisaniye)
rows=7000 (ilk parantez)
Planlayıcının TAHMİN ettiği satır sayısı
rows=7000.00 (actual parantezi)
Bu adımda gerçekten işlenen satır sayısı
loops=1
Bu düğümün kaç kez çalıştırıldığı — Nested Loop içinde 1'den büyük olabilir

loops değeri 1'den büyükse gösterilen actual time tek bir tekrarın süresidir; toplam harcanan süreyi bulmak için bu değeri loops ile çarpmak gerekir. Nested loop join'lerde iç taraf her dış satır için yeniden çalıştığından bu çarpım gözden kaçtığında "neden bu sorgu böyle yavaş" sorusu yanıtsız kalır.

Ölçümün ikinci katmanı Planning Time ve Execution Time ayrımı. Planning Time, planlayıcının en iyi yolu bulmak için harcadığı süredir; Execution Time ise gerçek çalıştırma süresidir. Küçük ama sık çalışan sorgularda Planning Time'ın Execution Time'a yakın veya ondan büyük çıkması, planlayıcının gereksiz yere karmaşık bir yol denediğine işaret edebilir.

ANALYZE'a BUFFERS eklediğinde çıktıya Buffers: shared hit=36 read=6 gibi bir satır eklenir: hit, veriyi bellekteki paylaşımlı önbellekten (shared buffer cache) okuduğunu, read ise diskten okumak zorunda kaldığını gösterir. Yüksek read sayısı, index'in kendisi küçük olsa bile disk I/O yüzünden yavaş kalabileceğinin işaretidir.

Son olarak Rows Removed by Filter satırını gözden kaçırma: bu, index veya taramanın getirdiği satırlardan kaçının ek bir WHERE koşuluyla elendiğini gösterir. Bu sayı yüksekse, index'in seçiciliği düşük demektir — index'i genişletmek (composite yapmak) veya farklı bir kolona taşımak gerekebilir.

sql
1-- BUFFERS ile birlikte tam ölçüm
2EXPLAIN (ANALYZE, BUFFERS) SELECT id, status FROM orders WHERE customer_id = 4821 AND status = 'pending';

Composite index kararı tam olarak burada devreye giriyor: customer_id üzerinde tek kolonlu bir index varsa ama filtre iki kolonu birden kullanıyorsa, planlayıcı customer_id ile daralttıktan sonra status'u filtre olarak (Rows Removed by Filter) eler. Sorguyu gerçekten hızlandıracak olan, iki kolonu birlikte kapsayan bir composite index'tir.

Composite index ve sol-ön-ek kuralı

PostgreSQL, bir index'i birden fazla kolon üzerinde tanımlamana izin verir — buna composite (çok kolonlu / concatenated) index denir. Mantığı bir telefon rehberine benzetmek işe yarar: rehber önce soyadına, sonra adına göre sıralanır. Soyadını bilerek arama yapmak hızlıdır; ama sadece adı bilerek rehberde arama yapmaya çalışmak, rehberi baştan sona taramaktan farksızdır.

Aynı mantık composite index için de geçerli: index, kolonları tanımlandığı sırayla (soldan sağa) kullanır. Bir sorgu index'in en soldaki kolonunu (veya en soldaki birkaç kolonu birlikte) filtrede kullanıyorsa index devreye girer; ama sadece ikinci veya üçüncü kolonu tek başına filtrelemeye çalışırsan, planlayıcı o index'i etkili biçimde kullanamaz ve index'in tamamını taramak zorunda kalabilir — kazanç büyük ölçüde kaybolur. Buna genel olarak "soldan-öncelik" (leftmost prefix) davranışı denir.

sql
1-- (customer_id, status) sırasıyla oluşturulan bir composite index
2CREATE INDEX orders_customer_status_idx ON orders (customer_id, status);
3 
4-- Bu sorgu index'i tam kullanır: en soldaki kolon (customer_id) filtrede var
5SELECT * FROM orders WHERE customer_id = 4821 AND status = 'pending';
6 
7-- Bu sorgu da index'i kullanabilir: yalnızca en soldaki kolon filtrede
8SELECT * FROM orders WHERE customer_id = 4821;
9 
10-- Bu sorgu index'i ETKİLİ biçimde KULLANAMAZ: en soldaki kolon (customer_id) yok
11SELECT * FROM orders WHERE status = 'pending';

Pratik sonuç: composite index'te kolon sırasını, "hangi sorgu deseni bu index'i kullanacak" sorusuna göre seçmelisin — en sık ve en seçici filtre kolonunu en sola koy. İki farklı sorgu deseni iki farklı sıralama gerektiriyorsa, tek bir index her ikisine de hizmet edemez; iki ayrı index gerekebilir. Ama her yeni index bir sonraki bölümde anlatılan yazma maliyetini de beraberinde getirir, o yüzden kolon sırasını rastgele değil, gerçek sorgu loglarına (pg_stat_statements) bakarak seçmek gerekir.

Partial ve expression index'ler

Her index tüm tablo satırlarını kapsamak zorunda değil. PostgreSQL dokümantasyonu partial index'i şöyle tanımlıyor: "A partial index is an index built over a subset of a table; the subset is defined by a conditional expression (called the predicate of the partial index). The index contains entries only for those table rows that satisfy the predicate." Yani partial index, sadece belirli bir koşulu sağlayan satırları kapsayan, daha küçük ve daha hızlı bir index.

Dokümantasyonun gerekçesi net: "One major reason for using a partial index is to avoid indexing common values. Since a query searching for a common value ... will not use the index anyway, there is no point in keeping those rows in the index at all." Örneğin bir orders tablosunda status kolonunun büyük çoğunluğu completed ise ve sorguların çoğu status = 'pending' arıyorsa, sadece pending satırlarını kapsayan bir partial index hem küçük hem de çok daha seçici olur.

sql
1-- Yalnızca "pending" siparişleri kapsayan partial index
2CREATE INDEX orders_pending_idx ON orders (created_at)
3WHERE status = 'pending';
4 
5-- Dokümantasyondaki örnek desene benzer: kurum içi IP aralığını dışarıda bırakma
6CREATE INDEX access_log_client_ip_ix ON access_log (client_ip)
7WHERE NOT (client_ip > inet '192.168.100.0' AND
8 client_ip < inet '192.168.100.255');

Expression index ise farklı bir problemi çözer: index'lediğin şey ham bir kolon değil, o kolondan hesaplanan bir ifade olabilir. Dokümantasyon: "An index column need not be just a column of the underlying table, but can be a function or scalar expression computed from one or more columns of the table. This feature is useful to obtain fast access to tables based on the results of computations."

sql
1-- E-posta aramasını büyük/küçük harf duyarsız hızlandırmak için
2CREATE INDEX users_email_lower_idx ON users (lower(email));
3 
4-- Bu index ancak sorgu AYNI ifadeyi kullanırsa devreye girer
5SELECT * FROM users WHERE lower(email) = '[email protected]';

Kritik detay: expression index yalnızca sorgu tam olarak aynı ifadeyi (lower(email)) kullandığında çalışır. Sorguyu email ILIKE '[email protected]' şeklinde yazarsan, index'in var olması hiçbir işe yaramaz.

Index'in gizli maliyeti: yazma, bakım, disk

Index'ler bedava değil. PostgreSQL dokümantasyonu bunu açıkça söylüyor: "After an index is created, the system has to keep it synchronized with the table. This adds overhead to data manipulation operations." Yani her INSERT, her UPDATE, her DELETE, tabloyla birlikte o tablodaki tüm index'leri de güncellemek zorunda. Beş index'in olduğu bir tabloya satır eklemek, index'siz aynı tabloya eklemekten fiziksel olarak daha fazla iş gerektirir.

Ayrıca index oluşturma sürecinin kendisi de maliyetlidir: "By default, PostgreSQL allows reads (SELECT statements) to occur on the table in parallel with index creation, but writes (INSERT, UPDATE, DELETE) are blocked until the index build is finished." Büyük bir prod tablosunda düz CREATE INDEX çalıştırmak yazma trafiğini kilitleyebilir — bu yüzden CREATE INDEX CONCURRENTLY production ortamlarında tercih edilen yoldur (yazmaları bloklamaz, ama daha uzun sürer ve başarısız olursa geçersiz bir index bırakabilir).

Dokümantasyon ayrıca index'lerin HOT (Heap-Only Tuples) optimizasyonunu engelleyebileceğini not ediyor: "Indexes can also prevent the creation of heap-only tuples." HOT, bir satır güncellendiğinde index'lenmemiş kolonlar değiştiyse PostgreSQL'in tüm index'leri güncellemeden hızlı bir satır-içi güncelleme yapmasını sağlayan bir mekanizmadır; gereksiz index'ler bu kısayolu devre dışı bırakır.

Maliyet türü
Ne zaman hissedilir
Yazma yavaşlaması
Her INSERT/UPDATE/DELETE'te tüm index'ler senkronize edilir
Disk alanı
Her index kendi B-Tree yapısı için ayrı disk sayfaları tutar
Build süresi
CREATE INDEX büyük tabloda uzun sürebilir; CONCURRENTLY olmadan yazmaları kilitler
HOT kaybı
Gereksiz index'ler satır-içi hızlı güncelleme kısayolunu engelleyebilir
Planlayıcı yükü
Çok fazla index, planlayıcının en iyi yolu seçmesini yavaşlatabilir

Sonuç olarak dokümantasyonun kendi tavsiyesi net: "Therefore indexes that are seldom or never used in queries should be removed." Kullanılmayan bir index sadece yazma maliyeti biriktirir, okuma tarafında hiçbir karşılığı olmaz.

Yavaş sorgu avlama: pg_stat_statements ile pratik akış

Hangi kolona index koyman gerektiğini tahminle değil, ölçümle bulman gerekiyor. Bunun standart aracı pg_stat_statements modülü. Dokümantasyon: "The pg_stat_statements module provides a means for tracking planning and execution statistics of all SQL statements executed by a server." Modül, sunucuda çalışan her benzersiz sorgu deseninin toplam çağrı sayısını, toplam ve ortalama süresini tutar.

Aktifleştirmek konfigürasyon dosyasında bir değişiklik gerektirir: "The module must be loaded by adding pg_stat_statements to shared_preload_libraries in postgresql.conf, because it requires additional shared memory."

ini
1# postgresql.conf
2shared_preload_libraries = 'pg_stat_statements'
3compute_query_id = on
4pg_stat_statements.max = 10000
5pg_stat_statements.track = all

Aktifleştirdikten sonra (ve sunucuyu yeniden başlattıktan sonra) pratik akış şöyle işler:

sql
1-- 1) Uzantıyı etkinleştir
2CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
3 
4-- 2) En çok toplam süre tüketen sorguları bul
5SELECT query, calls, total_exec_time, mean_exec_time, rows
6FROM pg_stat_statements
7ORDER BY total_exec_time DESC
8LIMIT 10;
9 
10-- 3) Şüpheli sorguyu EXPLAIN (ANALYZE, BUFFERS) ile aç ve Seq Scan'i doğrula
11-- 4) İlgili kolona/kolonlara index ekle, aynı sorguyu tekrar EXPLAIN et
12-- 5) pg_stat_statements'ı sıfırlayıp bir süre sonra tekrar ölç
13SELECT pg_stat_statements_reset();

calls çağrı sayısını, total_exec_time toplam harcanan milisaniyeyi, mean_exec_time ortalama süreyi, rows işlenen toplam satır sayısını gösterir. Buradaki mantık basit: az çağrılan ama her seferinde çok yavaş olan bir sorgu ile, sık çağrılan ve tek başına hızlı ama toplamda büyük yük oluşturan bir sorgu, total_exec_time'a göre sıralandığında ikisi de görünür hale gelir — index kararlarını "bana yavaş geldi" hissine değil, bu tabloya dayandırmak gerekir.

ORM'in ürettiği sorguyu görmek

Prisma gibi bir ORM kullanıyorsan, yavaş sorgunun kaynağı çoğu zaman ORM'in arka planda ürettiği SQL'i hiç görmemiş olmandır. Prisma Client, bağlantı kurulurken log seçeneğiyle üretilen sorguları konsola yazdırabilir:

ts
1// PrismaClient örneği oluşturulurken sorgu loglama açılır
2const prisma = new PrismaClient({
3 log: ["query"],
4});
5 
6// Artık her prisma.* çağrısında üretilen ham SQL konsola düşer
7const orders = await prisma.order.findMany({
8 where: { customerId: 4821, status: "pending" },
9 orderBy: { createdAt: "desc" },
10});

Bu logu açtığında genelde iki şey fark edersin: birincisi, ORM'in WHERE customerId = $1 AND status = $2 şeklinde ürettiği sorgunun tam olarak yukarıdaki composite index'in soldan-öncelik desenine uyup uymadığı; ikincisi, tek bir sayfa render'ında beklenmedik sayıda ayrı sorgu (N+1 problemi) çalışıp çalışmadığı. İkisi de index eklemeden önce görülmesi gereken şeyler — çünkü N+1 problemini index çözmez, sorgu yapısını (include/select ile ilişkileri tek sorguda çekmek) değiştirmek çözer.

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

Okuyucu Ödülü

Bu makaleyi sonuna kadar okuduğun için, index eklerken production'da kontrol etmen gereken kısa bir sağlama listesini aşağıda topladım. Bunu bir sonraki index kararında kullanabilirsin.

SSS

Hangi kolona index koymalıyım?

Öncelik sırası şu şekilde olmalı: WHERE ve JOIN koşullarında sık geçen, seçiciliği yüksek (az satırı işaret eden) kolonlar; foreign key kolonları; ve sık kullanılan ORDER BY kolonları. Tahmine dayanmak yerine pg_stat_statements çıktısında total_exec_time'a göre sıralanan sorguları incele ve o sorguların WHERE/JOIN kolonlarını hedefle.

EXPLAIN ANALYZE çıktısı nasıl okunur?

Üst satırdaki düğüm tipine (Seq Scan mi Index Scan mı) bak, sonra actual time=başlangıç..bitiş değerini loops sayısıyla çarparak gerçek toplam süreyi hesapla, rows değerinin planlayıcının tahminine (ilk parantez içindeki cost=... rows= değeri) ne kadar yakın olduğuna bak ve BUFFERS ekliyken read sayısının yüksek olup olmadığını kontrol et. Yüksek read, disk I/O'nun darboğaz olduğunu gösterir.

Composite index'te kolon sırası neden önemli?

Çünkü composite index kolonları soldan sağa doğru kullanılır — en soldaki kolon (veya en soldaki birkaç kolon birlikte) sorgunun filtresinde yoksa, planlayıcı o index'i etkili biçimde kullanamaz. Bu yüzden kolon sırası, index'i en çok hangi sorgu deseninin kullanacağına göre seçilmeli.

Çok fazla index yazma performansını ne kadar düşürür?

PostgreSQL dokümantasyonunun belirttiği gibi, her index oluşturulan tabloyla senkron tutulmak zorunda ve bu tüm veri değiştirme (INSERT/UPDATE/DELETE) işlemlerine ek yük bindirir. Kesin bir yüzde vermek yanıltıcı olur çünkü etki index sayısına, tablo boyutuna ve yazma sıklığına göre değişir; bu yüzden kesin rakam yerine pg_stat_statements ve gerçek yük testiyle ölçmek gerekir. Dokümantasyonun net tavsiyesi, seyrek veya hiç kullanılmayan index'lerin kaldırılması.

Index oluştururken tablo kilitlenir mi?

Varsayılan CREATE INDEX komutu, index build tamamlanana kadar tablo üzerindeki yazma işlemlerini (INSERT/UPDATE/DELETE) bloklar; okuma (SELECT) işlemleri bu sırada devam edebilir. Production'da yazmaları bloklamadan index eklemek için CREATE INDEX CONCURRENTLY kullanılır; bunun bedeli daha uzun build süresi ve olası bir hata durumunda geçersiz (invalid) bir index bırakma riskidir.

Güncelleme (Eylül 2026)

Bu makale 2025 başındaki PostgreSQL sürümüne göre yazıldı. 25 Eylül 2025'te yayınlanan PostgreSQL 18, bu makaledeki iki konuyu doğrudan etkileyen değişiklikler getirdi.

Birincisi, EXPLAIN ANALYZE artık BUFFERS bilgisini otomatik olarak gösteriyor — resmi sürüm notlarında "Automatically include BUFFERS output in EXPLAIN ANALYZE" olarak geçiyor. Yani makalede anlattığım EXPLAIN (ANALYZE, BUFFERS) şeklindeki elle ekleme artık PostgreSQL 18'de gerekli değil, hit/read bilgisi varsayılan çıktıda geliyor. Ayrıca sürüm notları, her index scan düğümü için kaç kez dizin araması (index lookup) yapıldığının da raporlandığını belirtiyor: "report the number of index lookups used per index scan node." Ayrıca çıktı biçiminde de bir değişiklik var: sürüm notlarında "Modify EXPLAIN to output fractional row counts" olarak geçiyor — PostgreSQL 18'den itibaren actual satırındaki rows değeri rows=7000.00 gibi iki ondalıkla basılıyor, önceki sürümlerde rows=7000 şeklindeydi (bu yazının başındaki tenk1 örneğindeki actual time=0.030..1.995 rows=7000.00 loops=1 satırı güncel dokümantasyondan alındığı için bu PostgreSQL 18 biçimini yansıtıyor; yazının ilk yayınlandığı dönemde aynı çıktı ondalıksız, rows=7000 olarak basılıyordu).

İkincisi ve daha çarpıcı olanı, composite index bölümünde anlattığım soldan-öncelik kuralına PostgreSQL 18 bir esneklik getirdi: B-tree "skip scan" desteği. Sürüm notları bunu şöyle özetliyor: "Allow skip scans of btree indexes... This allows multi-column btree indexes to be used in more cases such as when there are no restrictions on the first or early indexed columns (or there are non-equality ones), and there are useful restrictions on later indexed columns." Pratik anlamı şu: PostgreSQL 18 öncesinde makaledeki (customer_id, status) index'i sadece status = 'pending' filtresiyle etkili kullanılamıyordu; PostgreSQL 18'den itibaren planlayıcı belirli koşullarda bu index'i yine de kullanabiliyor, çünkü en soldaki kolonun az sayıda farklı değeri varsa planlayıcı o değerleri sırayla "atlayarak" tarayabiliyor. Bu, soldan-öncelik kuralını geçersiz kılmıyor — hâlâ en soldaki kolonu filtreye dahil etmek en güvenilir ve öngörülebilir yol — ama katı bir zorunluluk olmaktan çıkarıp bazı senaryolarda bir optimizasyon fırsatına dönüştürdü.

Sonuç

Index eklemek, EXPLAIN ANALYZE çalıştırmak kadar kolay görünür ama gerçek kazanç, ölçüm yapmadan ekleme arasındaki farkta saklıdır. Bu rehberde üç şeyi birbirine bağlamaya çalıştım: index'in ne zaman gerçekten yardımcı olduğu, EXPLAIN ANALYZE çıktısını doğru okumak, ve composite/partial/expression index çeşitlerini doğru senaryoda kullanmak.

Bir sonraki adım için, PostgreSQL'in kendisiyle Core Data gibi farklı bir veri katmanının performans yaklaşımını karşılaştırmak isteyebilirsin: Core Data İleri Seviye: Migration, Performance ve CloudKit Sync yazısı, benzer index/sorgu optimizasyonu mantığının mobil tarafta nasıl uygulandığını gösteriyor. Backend tarafında sorgu performansının API tasarımıyla nasıl kesiştiğini görmek için REST API Tasarım Prensipleri yazısına bakabilirsin. Sunucu tarafında Swift kullanan farklı bir backend yaklaşımı için Swift Vapor ile Backend API Geliştirme faydalı olacaktır. Bulut tabanlı bir veri senkronizasyon modelini görmek istersen CloudKit ile Veri Senkronizasyonu yazısına bakabilirsin. Son olarak, sorgu/ağ performansı disiplinini istemci tarafında uygulamak için Network Layer Optimizasyonu yazısı, bu makaledeki "önce ölç, sonra optimize et" prensibini farklı bir katmanda tekrarlıyor.

Kaynaklar

Etiketler

#PostgreSQL#index#query performansı#EXPLAIN ANALYZE#composite index#backend#veritabanı optimizasyonu
Muhittin Çamdalı

Muhittin Çamdalı

Lead Mobile Engineer

12+ yıllık deneyime sahip Lead Mobile Engineer. Swift, SwiftUI, Kotlin ve Flutter ile iOS, Android ve cross-platform mimarilerde uzman. Performanslı ve kullanıcı dostu mobil uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

İlgili İçerik