PostgreSQL partition pruning davranışını EXPLAIN, pg_stat_statements ve buffer metrikleriyle ölçün. Bu veritabanı optimizasyonu rehberi, predicate biçimi, prepared statement ve database indexleme kararlarının gerçek maliyetini inceler.
Veritabanı Optimizasyonu: PostgreSQL Partition Pruning ile Sorgu Planı
Veritabanı optimizasyonu için önce yanlış planı kanıtlayın
Partition pruning problemi, sorgunun yavaş olmasından çok planın gereksiz partition açmasından anlaşılır. Staging ortamında pg_stat_statements ile hedef sorgunun mean_exec_time, calls, shared_blks_read ve temp_blks_read değerlerini kaydedin. Aynı sorguyu en az 30 kez çalıştırıp ortalama yerine p95 gecikmesini de uygulama APM'inizden alın; tek bir disk cache ısınmış çalıştırması yanlış sonuç verir.
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
SELECT queryid,
calls,
round(mean_exec_time::numeric, 2) AS mean_ms,
round(max_exec_time::numeric, 2) AS max_ms,
shared_blks_hit,
shared_blks_read
FROM pg_stat_statements
WHERE query ILIKE '%FROM orders%'
ORDER BY total_exec_time DESC
LIMIT 10;Ardından aynı parametrelerle EXPLAIN (ANALYZE, BUFFERS, SETTINGS) alın. Plan altında 24 aylık tablo için 24 adet Seq Scan on orders_... görüyorsanız pruning çalışmamıştır; doğru plan yalnızca tarih aralığının kestiği partition'ları açmalıdır. Buffers: shared read değeri, planın fiziksel olarak kaç blok okuduğunu gösterir; yalnızca toplam süreye bakmak, cache durumu değiştiğinde hatalı bir veritabanı performans ayarlama kararı üretir.
SQL eğitimi: partition pruning'i bozan predicate biçimleri
Pruning'in temel şartı, partition anahtarının karşılaştırmada doğrudan görünmesidir. Aşağıdaki örnekte orders tablosu created_at üzerinden aylık RANGE partition'lara ayrılır. Uygulama sorgularında zaman aralığını kapalı-açık aralık olarak yazın: >= başlangıç ve < bitiş. Bu biçim milisaniye hassasiyetindeki kayıtları kaçırmaz ve yaz saati geçişlerindeki yerel gün sınırlarını timestamptz ile güvenli tutar.
CREATE TABLE orders (
id bigint GENERATED ALWAYS AS IDENTITY,
created_at timestamptz NOT NULL,
tenant_id bigint NOT NULL,
total_cents integer NOT NULL,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);
CREATE TABLE orders_2026_09 PARTITION OF orders
FOR VALUES FROM ('2026-09-01 00:00:00+00')
TO ('2026-10-01 00:00:00+00');
-- Pruning'e uygun: partition anahtarı sargable kalır
SELECT id, total_cents
FROM orders
WHERE created_at >= '2026-09-10 00:00:00+00'
AND created_at < '2026-09-11 00:00:00+00'
AND tenant_id = 42;
-- Pruning'i ve indeks erişimini bozabilir
SELECT id, total_cents
FROM orders
WHERE created_at::date = DATE '2026-09-10'
AND tenant_id = 42;İkinci sorguda PostgreSQL, her satırın created_at değerini önce date'e dönüştürmek zorunda kalır. Planner'ın partition constraint'iyle doğrudan kıyaslayacağı çıplak bir aralık kalmadığı için partition eleme fırsatı daralır. Bu, yalnızca SQL eğitimi sırasında anlatılan 'fonksiyon indeks kullanmaz' kuralından daha somuttur: fonksiyon hem partition constraint ispatını hem de normal B-tree sıralamasını etkiler. İş gereği gün bazlı API zorunluysa uygulama katmanında UTC gün başlangıcı ve ertesi gün başlangıcını üretin; expression index eklemeyi ilk çözüm olarak seçmeyin.
Veritabanı performans ayarlama: önce-sonra ölçümünü tekrarlanabilir kurun
Karşılaştırmayı aynı veri dağılımı, aynı bağlantı ve aynı parametre setiyle yapın. Test ortamında istatistikleri sıfırladıktan sonra önce fonksiyonlu predicate'i, sonra aralık predicate'ini 50 kez çalıştırın. pg_stat_statements_reset() tüm oturumların sayaçlarını siler; bu nedenle paylaşılan production ortamında değil, izole test veritabanında kullanılmalıdır.
SELECT pg_stat_statements_reset();
EXPLAIN (ANALYZE, BUFFERS, SETTINGS)
SELECT count(*)
FROM orders
WHERE created_at::date = DATE '2026-09-10'
AND tenant_id = 42;
EXPLAIN (ANALYZE, BUFFERS, SETTINGS)
SELECT count(*)
FROM orders
WHERE created_at >= TIMESTAMPTZ '2026-09-10 00:00:00+00'
AND created_at < TIMESTAMPTZ '2026-09-11 00:00:00+00'
AND tenant_id = 42;Sonuç tablosuna en az şu dört alanı yazın: açılan partition sayısı, Execution Time, shared_blks_read ve dönen satır sayısı. Örneğin 18 partition yerine 1 partition açılması tek başına başarı değildir; seçilen partition içindeki 8 milyon satır hâlâ sequential scan ile okunuyorsa ikinci problem yerel indeks eksikliğidir. Planın tahmini satır sayısı ile actual rows arasında 10 kattan büyük fark varsa, ANALYZE orders_2026_09; çalıştırın ve tarih-tenant korelasyonunu ayrıca inceleyin.
Database indexleme: partition içindeki erişim yolunu ayrı tasarlayın
Pruning partition sayısını azaltır, ancak tek bir partition içindeki filtreyi otomatik olarak ucuzlatmaz. Sorgu deseni tenant filtresi ve zaman sıralaması taşıyorsa, her aktif partition üzerinde (tenant_id, created_at DESC) B-tree indeksi kullanın. Eşitlik filtresi olan tenant_id'nin ilk sırada olması, belirli tenant'ın zaman aralığını dar bir indeks aralığına indirir; yalnızca created_at ile başlayan indeks bu sorguda tenant satırlarını sonradan filtreler.
CREATE INDEX CONCURRENTLY orders_2026_09_tenant_created_idx
ON orders_2026_09 (tenant_id, created_at DESC)
INCLUDE (total_cents);
EXPLAIN (ANALYZE, BUFFERS)
SELECT created_at, total_cents
FROM orders
WHERE tenant_id = 42
AND created_at >= TIMESTAMPTZ '2026-09-10 00:00:00+00'
AND created_at < TIMESTAMPTZ '2026-09-11 00:00:00+00'
ORDER BY created_at DESC
LIMIT 100;Bu database indexleme değişikliğinden sonra planda Index Only Scan görmeniz yeterli değildir; Heap Fetches değerini de kontrol edin. Yoğun UPDATE veya DELETE yapılan partition'larda visibility map bitleri temizlendiği için indeks yalnızca tarama olsa bile heap sayfalarına gidilir. Heap Fetches yüksekse vakum gecikmesini pg_stat_user_tables.n_dead_tup ve last_autovacuum ile inceleyin. Ayrıca CREATE INDEX CONCURRENTLY bir transaction block içinde çalışmaz; migration aracınız tüm migration'ı tek transaction'a sarıyorsa bu komutu transaction dışı migration olarak işaretleyin.
SQL Server eğitimi ve nosql eğitimi bağlamında partition eleme sınırları
Prepared statement kullanan PostgreSQL uygulamalarında hem generic hem custom planı test edin. Direct parametreli tarih koşulunda execution-time pruning çalışabilir ve EXPLAIN ANALYZE çıktısında Subplans Removed görünür. Buna karşılık parametreyi bir fonksiyonun içine saklamak veya türü belirsiz bırakmak planner'ın constraint çıkarımını zayıflatabilir. Aşağıdaki komutlar, uygulamanın normal plan seçimini kalıcı olarak değiştirmek için değil, iki davranışı karşılaştırmak içindir.
SET plan_cache_mode = force_generic_plan;
PREPARE orders_window(timestamptz, timestamptz, bigint) AS
SELECT count(*)
FROM orders
WHERE created_at >= $1
AND created_at < $2
AND tenant_id = $3;
EXPLAIN (ANALYZE, BUFFERS)
EXECUTE orders_window('2026-09-10 00:00:00+00',
'2026-09-11 00:00:00+00', 42);
DEALLOCATE orders_window;SQL Server eğitimi açısından aynı ilke partition elimination olarak izlenir: actual execution plan ile birlikte SET STATISTICS IO, TIME ON; açın ve partition anahtarında dönüştürme yapmadan CreatedAt >= @from AND CreatedAt < @to koşulunu ölçün. nosql eğitimi tarafında MongoDB shard key veya time-series bucket seçimi benzer görünse de aynı mekanizma değildir; db.orders.explain('executionStats').find({tenantId:42, createdAt:{$gte:ISODate('2026-09-10T00:00:00Z'),$lt:ISODate('2026-09-11T00:00:00Z')}}) ile yönlendirilen shard sayısını ve totalDocsExamined değerini ayrı doğrulamak gerekir. PostgreSQL'de DEFAULT partition kullanıyorsanız özellikle dikkat edin: yanlış tarihle gelen kayıtlar oraya düştüğünde, beklediğiniz aylık partition yerine büyüyen DEFAULT partition taranabilir.
İlgili Eğitim
Sık Sorulan Sorular
Veritabanı optimizasyonu için PostgreSQL partition pruning çalıştığını nasıl doğrularım?
Hedef sorguyu EXPLAIN (ANALYZE, BUFFERS) ile çalıştırın. Plan yalnızca tarih aralığının kestiği child table'ları içermeli; prepared statement senaryosunda ayrıca Subplans Removed satırını kontrol etmelisiniz. Önce-sonra kıyasına açılan partition sayısı, shared_blks_read ve p95 süreyi ekleyin.
Database indexleme partition edilmiş tabloda parent tabloya mı child tabloya mı yapılmalı?
Aktif partition'ların her birinde sorgu desenine uygun indeks bulunduğunu doğrulayın. Örneğin tenant_id = ? ve tarih aralığı için (tenant_id, created_at DESC) kullanın. pg_indexes ile child indekslerini listeleyin, ardından gerçek planda Index Scan veya düşük heap fetch'li Index Only Scan oluştuğunu kontrol edin.
SQL eğitimi kapsamında created_at::date neden partition performansını düşürür?
Cast, partition anahtarını ifade içine sarar. Planner, created_at::date = DATE '2026-09-10' koşulunu partition sınırlarıyla her durumda doğrudan ispatlayamaz; ayrıca normal created_at B-tree indeksindeki sıralı arama da bozulur. API tarih girdisini UTC başlangıç ve ertesi gün başlangıcına çevirip kapalı-açık aralık gönderin.
Veritabanı performans ayarlama sırasında generic prepared plan nasıl test edilir?
SET plan_cache_mode = force_generic_plan ile yalnızca test oturumunda generic planı zorlayın, sonra PREPARE, EXPLAIN (ANALYZE, BUFFERS) ve EXECUTE kullanın. Aynı sorguyu force_custom_plan ile tekrar ölçerek partition sayısı, buffer okumaları ve süre farkını kaydedin; bu ayarı uygulama genelinde kalıcılaştırmayı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.


