• 1.11.2026 07:32:42
  • İbrahim Gökyar

Pratik rehber: sorgu plan stabilizasyonu, database indexleme ve ölçülebilir veritabanı performans ayarlama adımları. Hem SQL eğitimi hem NoSQL eğitimi bağlamında uygulanabilir komut ve profil örnekleri içerir.

Veritabanı optimizasyonu: Plan stabilizasyonu, adaptif indeks ve ölçüm yöntemi

Sorgu plan stabilizasyonu: neden ve nasıl ölçülür

Plan stabilizasyonu, istatistik değiştikçe veya parametre dağılımı farklılaştıkça sorgunun beklenmedik planlar seçmesini engeller. Postgres'te pg_stat_statements ile bir sorgunun plan değişimini tespit etmek için önce pg_stat_statements uzantısını etkinleştirin ve en yüksek variance'ı olan sorguları listeleyin:

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
SELECT queryid, query, calls, mean_time, stddev_time FROM pg_stat_statements ORDER BY stddev_time DESC LIMIT 10;
Bu komut gerçek bir araç örneğidir ve hangi sorguların plan stabilizasyonuna ihtiyacı olduğunu somut olarak gösterir.

Opendart Akademi

Bu konuyu uygulamalı öğrenmek ister misiniz?

Java Spring Boot Eğitimi (Hibernate & JPA) eğitimimizde canlı projelerle, eğitmen eşliğinde adım adım ilerliyorsunuz.

Eğitimi incele →Ücretsiz danışmanlık: WhatsApp

Önemli noktalar: Veritabanı optimizasyonu: Plan stabilizasyonu,...: Sorgu plan stabilizasyonu: neden ve nasıl ölçülür; Database indexleme: adaptif, multicolumn ve bakım komutları; Profiling yöntemi: ölçülebilir adımlar, araçlar ve before-after karşılaştırma; NoSQL eğitimi bağlamında index maliyeti ve MongoDB örnekleri; Otomasyon, bakım planı ve yaygın tuzaklar
Önemli noktalar: Veritabanı optimizasyonu: Plan stabilizasyonu,...

Plan değişimini doğrulamak için EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) kullanın; farklı parametre setleriyle önce/sonra karşılaştırması alın. Örnek:

-- önce
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT * FROM orders WHERE customer_id = 12345;

-- belirli planı zorlamak için pg_hint_plan kullanıyorsanız
SET local pg_hint_plan.enable_hint = on;
-- veya belirli join tipi kapatabilirsiniz
SET LOCAL enable_nestloop = off;
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE customer_id = 12345;
Bu akış, neden mekanik olarak farklı planların ortaya çıktığını ve nasıl kontrol edileceğini gösterir.

Neden: optimizer seçimini etkileyen faktörler cardinality tahminleri ve cost modelidir; parametreli sorgularda generic plan - custom plan farkı oluşur. SQL Server bağlamında benzer tespiti yaparken Query Store'u açıp plan değişikliklerini kaydedin:

ALTER DATABASE CURRENT SET QUERY_STORE = ON;
SELECT qs.query_id, MAX(rs.count_executions) AS execs, rs.last_execution_time
FROM sys.query_store_query_text qt
JOIN sys.query_store_query q ON qt.query_text_id = q.query_text_id
JOIN sys.query_store_plan p ON q.query_id = p.query_id
JOIN sys.query_store_runtime_stats rs ON p.plan_id = rs.plan_id
GROUP BY qs.query_id, rs.last_execution_time ORDER BY execs DESC;
Bu örnekler sorgu bazlı gerçek ölçüm adımlarıdır.

Database indexleme: adaptif, multicolumn ve bakım komutları

İndeks stratejisi oluştururken sorgu filtreleri, selectivity ve update yoğunluğunu birlikte değerlendirin. Postgres'te üretimde index eklerken downtime'ı azaltmak için CREATE INDEX CONCURRENTLY kullanın ve değişiklik sonrası pg_stat_user_indexes ile kullanım oranını kontrol edin:

CREATE INDEX CONCURRENTLY idx_orders_customer_created ON orders (customer_id, created_at DESC);
-- 24 saat sonra kullanım kontrolü
SELECT indexrelid::regclass AS index, idx_scan, idx_tup_read FROM pg_stat_user_indexes WHERE indexrelid::regclass = 'idx_orders_customer_created'::regclass;
Bu somut komutlar, database indexleme sürecinin operasyonel adımlarını gösterir.

Fillfactor, page-level bloat ve rebuild stratejisi gibi bakım tercihleri verimi doğrudan etkiler. Örneğin yüksek update oranı olan tablo için fillfactor düşürün ve REINDEX veya pg_repack planlayın:

ALTER TABLE orders SET (fillfactor = 70);
-- reindex online alternatifi
pg_repack --table=public.orders --dbname=mydb --host=dbhost
-- SQL Server için fragmantasyon kontrolü
SELECT avg_fragmentation_in_percent FROM sys.dm_db_index_physical_stats(DB_ID('mydb'), OBJECT_ID('dbo.orders'), NULL, NULL, 'LIMITED');
ALTER INDEX ALL ON dbo.orders REORGANIZE; -- veya REBUILD
Bu örnekler hem Postgres hem SQL Server bakım komutlarını içerir.

Edge case: çok kolonu içeren indeksler (multicolumn) yanlış sırada tanımlanırsa kullanılmaz; sorgu planında index-only scan beklerken heap fetch görülüyorsa visibility map veya included columns eksikliği olabilir. Kontrol için EXPLAIN (ANALYZE, BUFFERS) kullanın ve index'in prefix-sorgularda kullanılabilir olmasına dikkat edin:

-- yanlış: (a, b, c) sorgu WHERE b=... OR c=...
-- doğru: gereksinime göre ayrı B-tree veya GIN/BRIN index'ler oluşturun
CREATE INDEX idx_orders_b ON orders (b);
CREATE INDEX idx_orders_c ON orders (c);
CREATE INDEX idx_orders_gin ON orders USING gin (jsonb_column jsonb_path_ops);

Profiling yöntemi: ölçülebilir adımlar, araçlar ve before-after karşılaştırma

Profiling yaparken önce hedef metriği netleyin: wall-clock süre, CPU ms, logical reads veya lock wait gibi. Postgres örneğinde bir sorgunun önceki ve sonraki durumunu karşılaştırmak için EXPLAIN ANALYZE ve psql \\timing kombinasyonunu kullanın:

-- ölçüm öncesi
\timing on
EXPLAIN (ANALYZE, BUFFERS) SELECT count(*) FROM orders WHERE status='paid';
-- indeks ekleme
CREATE INDEX CONCURRENTLY idx_orders_status ON orders (status);
-- ölçüm sonrası
EXPLAIN (ANALYZE, BUFFERS) SELECT count(*) FROM orders WHERE status='paid';
\timing off
Örnek sonuç: önce 1200 ms wall-time, sonra 45 ms; bu tür somut rakamlar performans kazanımını doğrular.

Hangi aracı/nasıl kullanacağınız: Linux sunucuda perf/top/bcc (e.g. offcpu or hot functions) ve iostat/vmstat ile I/O beklemelerini ayırın; Postgres'te yoğun CPU mı yoksa I/O mı olduğunu anlamak için:

-- sistem düzeyi örnekler
sudo perf record -g -p  -- sleep 30
sudo perf report --stdio
sudo iostat -x 1 3
-- Postgres sorgu düzeyinde
SELECT * FROM pg_stat_activity WHERE state <> 'idle';
Bu adımlar CPU-bound vs IO-bound ayrımı ve sonra doğru optimizasyonu seçmek için gereklidir.

SQL Server için somut profil örneği: SET STATISTICS IO/TIME ile logical read ve CPU ölçümü alıp, Query Store ile plan değişimini gözleyin:

SET STATISTICS IO ON; SET STATISTICS TIME ON;
SELECT * FROM dbo.orders WHERE customer_id = 12345;
SET STATISTICS IO OFF; SET STATISTICS TIME OFF;
-- Query Store örneği
SELECT qsqt.query_sql_text, rs.avg_duration, rp.plan_id
FROM sys.query_store_query_text qsqt
JOIN sys.query_store_query q ON qsqt.query_text_id = q.query_text_id
JOIN sys.query_store_runtime_stats rs ON q.query_id = rs.query_id
JOIN sys.query_store_plan rp ON rs.plan_id = rp.plan_id
ORDER BY rs.avg_duration DESC;
Bu somut komutlar ile önce-sonra karşılaştırması yapın; rakamları kaydedin ve grafikleştirerek regressiyonları yakalayın.

Makalenin yol haritası: Veritabani optimizasyonu: Plan stabilizasyonu, ad…
Makalenin yol haritası: Veritabani optimizasyonu: Plan stabilizasyonu, ad…

NoSQL eğitimi bağlamında index maliyeti ve MongoDB örnekleri

NoSQL veritabanlarında da indexler okuma hızını artırırken yazma maliyetini yükseltir; MongoDB'de write amplification ölçmek için profiler ve explain kullanın. Örnek: bir index oluşturup sonra yazma benchmark'ı çalıştırın ve writeLatency/ops değerini karşılaştırın:

-- index oluşturma
db.orders.createIndex({ customer_id: 1, created_at: -1 });
-- profiler açma
db.setProfilingLevel(2);
-- benchmark (örnek Node.js script veya mongoperf)
-- sonra profiler sonuçlarını incele
db.system.profile.find({ns: 'mydb.orders', op: 'insert'}).sort({ts:-1}).limit(20).pretty();
-- explain ile sorgu maliyeti
db.orders.find({customer_id: 12345}).explain('executionStats');
Bu adımlar ile NoSQL indekslerinin gerçek maliyetini ölçün.

Edge case: MongoDB'de çok sayıda küçük index'in toplam maliyeti bellek ve checkpoint süresini arttırır. Somut optimizasyon: compound index kullanarak read pattern'lerini birleştirin veya TTL index yerine periyodik mikro-batch temizleme tercih edin. Örneğin TTL ile beklenmedik arka plan silmeleri görüyorsanız dakika hassasiyeti isteniyorsa şöyle yapın:

-- TTL örneği
db.sessions.createIndex({ lastAccess: 1 }, { expireAfterSeconds: 3600 });
-- alternatif: uygulama tarafında cron ile temizleme
db.sessions.deleteMany({ lastAccess: { $lt: ISODate('2026-01-01T00:00:00Z') } });

NoSQL eğitiminde dikkat: explain('executionStats') size dönülen docs, totalKeysExamined ve totalDocsExamined sayıları verir; bunlar index etkinliğinin ölçülebilir metrikleridir. Sistematik ölçüm yapın: test dataset'i kullanarak A/B benchmark (index vs no-index) çalıştırın ve medyan latenceleri karşılaştırın.

Otomasyon, bakım planı ve yaygın tuzaklar

İndeks yaşam döngüsünü otomatikleştirmek için günlük/haftalık ölçüm tabanlı kararlar alın: örneğin 7 gün içinde idx_scan=0 olan indeksleri raporlayıp manuel inceleme isteği oluşturun. Postgres için örnek rapor sorgusu:

SELECT schemaname, relname, indexrelname, idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes JOIN pg_index ON pg_stat_user_indexes.indexrelid = pg_index.indexrelid
WHERE idx_scan = 0 AND pg_relation_size(pg_stat_user_indexes.indexrelid) > 1024*1024;
-- bu çıktıyı cron job ile CSV'ye yazıp değerlendirin
Bu kesin, uygulanabilir otomasyon adımıdır.

Autovacuum ve bloat: yoğun update/delete tablo için autovacuum parametrelerini ayarlayın ve bloat ölçümü yapın. Örnek param değişikliği ve ölçüm komutları:

# postgresql.conf değişikliği (örnek)
autovacuum_vacuum_scale_factor = 0.01
autovacuum_max_workers = 10
-- bloat tespiti (pgstattuple veya query)
SELECT schemaname, tablename, (pg_total_relation_size(relid) - pg_relation_size(relid)) AS bloat_bytes
FROM pg_catalog.pg_stat_user_tables ORDER BY bloat_bytes DESC LIMIT 10;
Bu somut konfigürasyon ve sorgular bloat yönetimini işler.

Yaygın tuzak: sadece index sayısını artırmak yerine, sorgu patternlerini değiştirip uygulama tarafında sorgu şekillendirmeyi düşünün; örneğin IN (...) ile büyük liste kullanmak yerine temp table veya JOIN kullanmak daha stabil plan sağlar. Ölçü için örnek SQL Server pattern değişikliği:

-- kötü: WHERE id IN ( ... binlerce id ... )
-- alternatif: bulk insert to temp table
CREATE TABLE #ids (id INT PRIMARY KEY);
INSERT INTO #ids VALUES (1),(2),(3)...;
SELECT o.* FROM dbo.orders o JOIN #ids i ON o.id = i.id;

Öğrendiklerinizi gerçek bir projede uygulayın

Java Spring Boot Eğitimi (Hibernate & JPA) ile bu makaledeki konuları canlı derslerde, gerçek projeler üzerinde uygulayarak öğrenin. Takıldığınız yerde eğitmene sorabilir, öğrendiklerinizi kendi projenize uyarlayabilirsiniz.

Eğitimi incele →Ücretsiz danışmanlık: WhatsApp

Sık Sorulan Sorular

veritabanı optimizasyonu için önce hangi metriklere bakmalıyım?

Öncelik: sorgu wall-clock zamanı, CPU time, logical reads (Postgres: EXPLAIN ANALYZE BUFFERS ve pg_stat_statements), ve I/O bekleme (iostat). Somut adım: bir sorgu için EXPLAIN (ANALYZE, BUFFERS) çalıştırın ve psql ile \timing on kullanarak wall-time ile karşılaştırın; sistem seviyesinde sudo iostat -x 1 5 ve sudo perf record -p ile CPU profili alın.

database indexleme yaparken hangi durumlarda CREATE INDEX CONCURRENTLY tercih etmeliyim?

Canlı sistemde tabloya kilit vermemek için; Postgres'te büyük tabloya index eklerken CONCURRENTLY kullanın: CREATE INDEX CONCURRENTLY idx_name ON table(column); Ancak dikkat: CONCURRENTLY sırasında aynı index tekrar oluşturulamaz ve işlem uzun sürerse rollback riskine karşı izleme gerektirir. İş akışı: index oluştur, 24 saat sonra pg_stat_user_indexes ile idx_scan kontrol et, sonra eski indeksleri temizle.

sql server eğitimi kapsamında plan gerilemesini hızlıca nasıl tespit ederim?

Query Store'u açın ve top regressions raporlarını kullanın; hızlı tespit için önce SET STATISTICS IO/TIME ON ile sorgu ile ilgili logical reads ve CPU ölçün, sonra Query Store'dan plan_id bazlı avg_duration ve executions karşılaştırmasını alın. Somut komutlar: ALTER DATABASE CURRENT SET QUERY_STORE = ON; sonra sys.query_store_runtime_stats sorgularını çalıştırın.

nosql eğitimi alıyorum: MongoDB'de indexlerin yazma maliyetini nasıl ölçerim?

MongoDB profiler ve explain('executionStats') ile ölçün. Adımlar: db.setProfilingLevel(2); benchmark yazma yükünü çalıştırın (ör. mongoperf veya kendi scriptiniz), sonra db.system.profile.find({op: 'insert'}) ile gecikme ve I/O örneklerine bakın. Ayrıca index eklemeden/sonra aynı benchmark'ı çalıştırıp ops/s ve write latency değerlerini karşılaştırın.

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.

Opendart Akademi llms.txt