EXPLAIN, PostgreSQL planlayıcısının bir sorgu için seçtiği yürütme planını ağaç biçiminde gösterir: tablonun sıralı mı yoksa indeksle mi okunacağını, tabloların hangi birleştirme yöntemiyle bağlanacağını ve her adım için tahmini maliyeti ve satır sayısını. EXPLAIN ANALYZE ise sorguyu gerçekten çalıştırır ve tahminlerin yanına gerçek süreyi ve gerçek satır sayısını ekler. Plan okumanın özü, en alttaki tarama düğümlerinden yukarı doğru ilerlemek ve tahmin edilen satır sayısıyla gerçekleşen sayıyı karşılaştırmaktır.
Bu yazıda cost, rows ve width değerlerinin ne anlama geldiğini, tarama ve birleştirme düğümlerini, ANALYZE ve BUFFERS çıktısını ve eski istatistiklerin yol açtığı tahmin hatasını gerçek planlar üzerinden okuyoruz. Genel performans ayarları, indeks stratejisi ve bağlantı havuzu gibi konular PostgreSQL performans optimizasyonu yazısında, JOIN ve alt sorgu performansı ise SQL sorgu optimizasyonu yazısında ele alınıyor; burada mekanizmaya odaklanıyoruz.
Örnek veri
Planların anlamlı olması için biraz veri gerekiyor. 10.000 müşteri ve 100.000 sipariş içeren iki tablo oluşturup istatistikleri topladık. Veri generate_series ile deterministik üretildi; sizin denemenizde maliyetler aynı, süreler ise makineye göre farklı çıkacaktır:
CREATE TABLE musteri (
id int PRIMARY KEY,
ad text NOT NULL,
sehir text NOT NULL
);
INSERT INTO musteri
SELECT i, 'Müşteri ' || i,
(ARRAY['İstanbul','Ankara','İzmir','Bursa','Antalya'])[i % 5 + 1]
FROM generate_series(1, 10000) AS i;
CREATE TABLE siparis (
id int PRIMARY KEY,
musteri_id int NOT NULL REFERENCES musteri,
tutar numeric(10,2) NOT NULL,
tarih date NOT NULL
);
INSERT INTO siparis
SELECT i, (i % 10000) + 1, (i % 500) + 0.99, DATE '2026-01-01' + (i % 300)
FROM generate_series(1, 100000) AS i;
VACUUM ANALYZE musteri, siparis;EXPLAIN çıktısının dört sayısı

En basit plan, koşulsuz bir tablo okumasıdır:
EXPLAIN SELECT * FROM siparis;
SELECT relpages, reltuples FROM pg_class WHERE relname = 'siparis'; QUERY PLAN
----------------------------------------------------------------
Seq Scan on siparis (cost=0.00..1637.00 rows=100000 width=18)
(1 row)
relpages | reltuples
----------+-----------
637 | 100000
(1 row)Parantez içindeki değerler soldan sağa şunlardır:
- Başlangıç maliyeti (0.00): İlk satır üretilmeden önce harcanan tahmini iş; örneğin bir sıralama düğümünde sıralamanın kendisi.
- Toplam maliyet (1637.00): Düğüm sonuna kadar çalıştırılırsa, yani bütün satırlar alınırsa harcanacak tahmini iş.
- rows (100000): Düğümün çıktı olarak vereceği tahmini satır sayısı. Taranan değil, filtreden geçen satırlardır.
- width (18): Çıktı satırlarının bayt cinsinden tahmini ortalama genişliği.
Maliyetler milisaniye değildir; planlayıcının maliyet parametreleriyle belirlenen keyfi birimlerdir. Geleneksel olarak bir disk sayfasını sırayla okumanın maliyeti (seq_page_cost) 1.0 alınır ve diğer parametreler buna göre ayarlanır. Kılavuz sıralı taramanın maliyetini "okunan sayfa sayısı çarpı seq_page_cost artı taranan satır sayısı çarpı cpu_tuple_cost" olarak açıklar. Varsayılan değerlerle hesabı kendimiz yapabiliriz: 637 x 1.0 + 100000 x 0.01 = 1637. Planla birebir aynı.
İki önemli kural daha vardır. Bir üst düğümün maliyeti, altındaki bütün çocuk düğümlerin maliyetini içerir. Maliyet ayrıca çıktının metne dönüştürülmesini ve istemciye gönderilmesini hesaba katmaz; planlayıcı bunları plan seçimiyle değiştiremeyeceği için yok sayar.
Filtre eklemek ve EXPLAIN ANALYZE
Şimdi bir koşul ekleyip sorguyu ANALYZE seçeneğiyle gerçekten çalıştıralım:
EXPLAIN ANALYZE SELECT * FROM siparis WHERE tutar > 400; QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Seq Scan on siparis (cost=0.00..1887.00 rows=20101 width=18) (actual time=0.206..6.805 rows=20000.00 loops=1)
Filter: (tutar > '400'::numeric)
Rows Removed by Filter: 80000
Buffers: shared hit=637
Planning:
Buffers: shared hit=7
Planning Time: 0.072 ms
Execution Time: 7.220 ms
(8 rows)Bu çıktıda okunacak çok şey var:
- Filter: Koşul her satır için tarama sırasında kontrol ediliyor. Tahmini çıktı 20101 satıra düştü ama maliyet 1637'den 1887'ye arttı: yine 100.000 satırın hepsi okunacak ve her biri için bir karşılaştırma (
cpu_operator_cost, varsayılan 0.0025) eklenecek. 100000 x 0.0025 = 250. - actual time=0.206..6.805: İlk satırın ve son satırın üretildiği an, milisaniye cinsinden gerçek süre. Maliyet birimleriyle karşılaştırılamaz.
- rows=20000.00: Gerçek satır sayısı. Tahmin (20101) gerçeğe çok yakın; planın sağlıklı olduğunun en iyi işareti budur.
- loops=1: Düğümün kaç kez çalıştırıldığı. İç içe döngü birleştirmelerinde iç düğüm birçok kez çalışır; süre ve satır sayısı tek bir çalıştırmanın ortalamasıdır, toplam için
loopsile çarpmalısınız. - Rows Removed by Filter: Filtrenin elediği satır sayısı; yalnızca en az bir satır elendiğinde görünür.
- Buffers: Paylaşılan arabellekte bulunan (hit), diskten okunan (read), kirletilen (dirtied) ve yazılan (written) blok sayıları. Üst düğümün sayısı alt düğümleri de kapsar.
- Planning Time / Execution Time: Planlama süresi ayrıştırma ve yeniden yazmayı içermez; yürütme süresi tetikleyicileri içerir ama planlamayı içermez.
Arabellek bilgisini istemiyorsanız açıkça kapatabilirsiniz. Zamanlama ölçümünün kendisi de yük getirdiği için yalnızca satır sayılarına bakacaksanız TIMING OFF kullanabilirsiniz; SUMMARY OFF ise alttaki süre satırlarını kaldırır:
EXPLAIN (ANALYZE, BUFFERS OFF, TIMING OFF, SUMMARY OFF) SELECT * FROM siparis WHERE tutar > 400; QUERY PLAN
----------------------------------------------------------------------------------------------
Seq Scan on siparis (cost=0.00..1887.00 rows=20101 width=18) (actual rows=20000.00 loops=1)
Filter: (tutar > '400'::numeric)
Rows Removed by Filter: 80000
(3 rows)Tarama düğümleri: Seq, Index, Index Only ve Bitmap

Planın en altında tabloyu okuyan tarama düğümleri bulunur. Birincil anahtarla tek satır aranırken planlayıcı indeks taraması seçer; yalnızca indeksteki sütunlar isteniyorsa tabloya hiç gitmeden yanıt verebilir:
EXPLAIN SELECT * FROM siparis WHERE id = 42;
EXPLAIN SELECT id FROM siparis WHERE id BETWEEN 100 AND 120; QUERY PLAN
-----------------------------------------------------------------------------
Index Scan using siparis_pkey on siparis (cost=0.29..8.31 rows=1 width=18)
Index Cond: (id = 42)
(2 rows)
QUERY PLAN
----------------------------------------------------------------------------------
Index Only Scan using siparis_pkey on siparis (cost=0.29..4.71 rows=21 width=4)
Index Cond: ((id >= 100) AND (id <= 120))
(2 rows)Index Cond, koşulun indeks üzerinde uygulandığını gösterir; Filter ise satır getirildikten sonra yapılan kontroldür. Bir koşulun hangisinde göründüğü, indeksin o koşul için kullanılıp kullanılamadığını anlatır. Şimdi müşteri sütununa bir indeks ekleyip tek bir müşterinin siparişlerini isteyelim:
CREATE INDEX siparis_musteri_idx ON siparis (musteri_id);
EXPLAIN ANALYZE SELECT * FROM siparis WHERE musteri_id = 7; QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on siparis (cost=4.37..40.74 rows=10 width=18) (actual time=0.061..0.068 rows=10.00 loops=1)
Recheck Cond: (musteri_id = 7)
Heap Blocks: exact=10
Buffers: shared hit=10 read=2
-> Bitmap Index Scan on siparis_musteri_idx (cost=0.00..4.37 rows=10 width=0) (actual time=0.044..0.044 rows=10.00 loops=1)
Index Cond: (musteri_id = 7)
Index Searches: 1
Buffers: shared read=2
Planning:
Buffers: shared hit=16 read=1
Planning Time: 0.234 ms
Execution Time: 2.546 ms
(12 rows)Bu iki aşamalı bir plandır. Alttaki Bitmap Index Scan indeksten eşleşen satırların yerlerini toplar; üstteki Bitmap Heap Scan bu yerleri fiziksel sıraya dizip tablo sayfalarını okur. Kılavuz bu yaklaşımı şöyle açıklar: satırları tek tek getirmek sıralı okumadan pahalıdır, ama tablonun bütün sayfalarını gezmek gerekmediği için yine de sıralı taramadan ucuzdur; "bitmap" de bu sıralamayı yapan mekanizmadır. Bu müşterinin 10 siparişi tabloya dağınık yerleştiği için (Heap Blocks: exact=10) planlayıcı bu yolu seçti. Buffers satırındaki read=2, yeni oluşturulan indeksin iki sayfasının paylaşılan arabellekte bulunmadığı için okunması gerektiğini söylüyor.
| Düğüm | Ne yapar | Ne zaman görülür |
|---|---|---|
| Seq Scan | Tablonun bütün sayfalarını sırayla okur | Koşulsuz veya satırların büyük kısmını döndüren sorgular, küçük tablolar |
| Index Scan | İndeksi izleyip satırları indeks sırasıyla getirir | Çok az satır, indeks sırasına uyan ORDER BY |
| Index Only Scan | Yanıtı yalnızca indeksten verir, görünürlük haritası izin verirse tabloya gitmez | İstenen bütün sütunlar indekste ise |
| Bitmap Index Scan + Bitmap Heap Scan | Önce satır yerlerini toplar, sonra sayfaları fiziksel sırayla okur | Orta sayıda, tabloya dağılmış satır |
| Sort / Incremental Sort | Satırları sıralar | İndeksle karşılanamayan ORDER BY, birleştirme hazırlığı |
| Limit | İstenen sayıda satırdan sonra durur | LIMIT içeren sorgular |
Hangi indeks türünün hangi tarama düğümünü mümkün kıldığı ve çok sütunlu, kısmi ya da ifade indeksleri B-tree, GIN, GiST ve BRIN yazısında anlatılıyor.
Birleştirme düğümleri: Hash Join, Nested Loop, Merge Join
Birden çok tablo içeren sorgularda tarama düğümlerinin üzerinde birleştirme düğümleri yer alır. Girinti, ağacın yapısını gösterir: bir düğümün hemen altındaki daha içeride yazılmış satırlar onun çocuklarıdır. Maliyetleri gizleyip yalnızca yapıya bakmak için COSTS OFF kullanalım:
EXPLAIN (COSTS OFF)
SELECT m.sehir, sum(s.tutar)
FROM siparis s JOIN musteri m ON m.id = s.musteri_id
GROUP BY m.sehir;
EXPLAIN (COSTS OFF)
SELECT m.ad, s.tutar
FROM musteri m JOIN siparis s ON s.musteri_id = m.id
WHERE m.id = 7; QUERY PLAN
------------------------------------------
HashAggregate
Group Key: m.sehir
-> Hash Join
Hash Cond: (s.musteri_id = m.id)
-> Seq Scan on siparis s
-> Hash
-> Seq Scan on musteri m
(7 rows)
QUERY PLAN
------------------------------------------------------
Nested Loop
-> Index Scan using musteri_pkey on musteri m
Index Cond: (id = 7)
-> Bitmap Heap Scan on siparis s
Recheck Cond: (musteri_id = 7)
-> Bitmap Index Scan on siparis_musteri_idx
Index Cond: (musteri_id = 7)
(7 rows)Birinci sorguda bütün siparişler gerektiği için planlayıcı hash join seçti: Hash düğümü müşteri tablosundan bellekte bir hash tablosu kuruyor, Hash Join ise sipariş tablosunu tarayıp her satır için bu tabloya bakıyor. Sonucu HashAggregate şehirlere göre topluyor. İkinci sorguda tek bir müşteri istendiği için nested loop seçildi: dış (ilk) çocuk her satır ürettiğinde iç (ikinci) çocuk bir kez çalışıyor. Dikkat ederseniz planlayıcı, m.id = 7 ve birleştirme koşulundan s.musteri_id = 7 sonucunu çıkarıp iç taramaya doğrudan uygulamış.
Üçüncü yöntem merge join'dir; iki girdinin birleştirme anahtarına göre sıralı olmasını ister ve sıralı iki listeyi yan yana yürür. Kılavuz, dış birleştirmelerde Join Filter ile düz Filter ayrımına da dikkat çeker: Join Filter ON koşulundan gelir ve onu geçemeyen satır yine NULL ile doldurulmuş olarak çıkabilir; Filter ise satırı koşulsuz eler.
LIMIT, sıralama ve başlangıç maliyeti
EXPLAIN ANALYZE SELECT * FROM siparis ORDER BY tutar DESC LIMIT 5; QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------
Limit (cost=3297.96..3297.98 rows=5 width=18) (actual time=8.030..8.031 rows=5.00 loops=1)
Buffers: shared hit=640
-> Sort (cost=3297.96..3547.96 rows=100000 width=18) (actual time=8.028..8.029 rows=5.00 loops=1)
Sort Key: tutar DESC
Sort Method: top-N heapsort Memory: 25kB
Buffers: shared hit=640
-> Seq Scan on siparis (cost=0.00..1637.00 rows=100000 width=18) (actual time=0.007..2.507 rows=100000.00 loops=1)
Buffers: shared hit=637
Planning:
Buffers: shared hit=12
Planning Time: 0.040 ms
Execution Time: 8.045 ms
(12 rows)Burada başlangıç maliyetinin anlamı netleşiyor: Sort düğümü bütün girdiyi okumadan ilk satırı veremeyeceği için başlangıç maliyeti (3297.96) neredeyse toplam maliyet kadar yüksek. Sort Method: top-N heapsort Memory: 25kB satırı, yalnızca en büyük 5 değer istendiği için sıralamanın küçük bir yığınla bellekte yapıldığını gösteriyor. Sıralama belleğe sığmasaydı burada diske taşan bir yöntem görürdünüz. Ayrıca Sort düğümünün tahmini rows=100000 değeri düğüm sonuna kadar çalışsaydı olacak sayıdır; gerçekte Limit 5 satırdan sonra durduğu için actual rows=5.00 görünüyor. Kılavuz bunun bir tahmin hatası değil, gösterim farkı olduğunu özellikle belirtir.
Tahmin hatası: eski istatistiklerin bedeli
Planlayıcı kararlarını ANALYZE komutunun topladığı istatistiklere göre verir. İstatistikler güncel değilse tahminler ve dolayısıyla plan yanlış olabilir. Bunu göstermek için autovacuum'u kapalı bir tablo oluşturduk, istatistikleri tüm satırlar "aktif" iken topladık ve ardından satırların %80'ini "pasif" yaptık:
CREATE TABLE urun (id int PRIMARY KEY, durum text NOT NULL) WITH (autovacuum_enabled = off);
INSERT INTO urun SELECT i, 'aktif' FROM generate_series(1, 50000) i;
CREATE INDEX urun_durum_idx ON urun (durum);
ANALYZE urun;
UPDATE urun SET durum = 'pasif' WHERE id <= 40000;
EXPLAIN (ANALYZE, BUFFERS OFF) SELECT count(*) FROM urun WHERE durum = 'pasif';
ANALYZE urun;
EXPLAIN (ANALYZE, BUFFERS OFF) SELECT count(*) FROM urun WHERE durum = 'pasif'; QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=4.31..4.32 rows=1 width=8) (actual time=5.446..5.447 rows=1.00 loops=1)
-> Index Only Scan using urun_durum_idx on urun (cost=0.29..4.31 rows=1 width=0) (actual time=0.094..4.341 rows=40000.00 loops=1)
Index Cond: (durum = 'pasif'::text)
Heap Fetches: 40000
Index Searches: 1
Planning Time: 0.130 ms
Execution Time: 5.467 ms
(7 rows)
ANALYZE
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Aggregate (cost=1212.15..1212.16 rows=1 width=8) (actual time=5.270..5.271 rows=1.00 loops=1)
-> Seq Scan on urun (cost=0.00..1112.00 rows=40062 width=0) (actual time=1.754..4.244 rows=40000.00 loops=1)
Filter: (durum = 'pasif'::text)
Rows Removed by Filter: 10000
Planning Time: 0.376 ms
Execution Time: 5.291 ms
(6 rows)İlk planda planlayıcı "pasif" değerinin neredeyse hiç bulunmadığını sanıyor (rows=1), gerçekte ise 40.000 satır geldi. Bu kadar büyük bir sapma, planın yanlış varsayımla kurulduğunu gösterir. Üstelik Heap Fetches: 40000 satırı, "yalnızca indeks" taramasının aslında her satır için tabloya gittiğini söylüyor; çünkü güncellenen sayfalar görünürlük haritasında henüz "herkese görünür" olarak işaretlenmemişti. ANALYZE sonrasında tahmin 40062'ye çıktı ve planlayıcı sıralı taramaya geçti. Bu küçük tabloda süre farkı önemsiz, ama milyonlarca satırlık bir tabloda veya birleştirmelerde yanlış tahmin yanlış birleştirme yöntemine ve çok daha uzun sürelere yol açabilir.
Kılavuzun EXPLAIN sayfası, planlayıcının makul kararlar verebilmesi için istatistiklerin güncel olması gerektiğini, bunu normalde autovacuum'un yaptığını, ama büyük değişikliklerden sonra elle ANALYZE çalıştırmanın gerekebileceğini belirtir. Görünürlük haritası ve autovacuum'un istatistik toplama eşikleri VACUUM ve autovacuum yazısında anlatılıyor. Planlayıcının satır tahminlerini nasıl hesapladığını ayrıntılı görmek isterseniz kılavuzun Row Estimation Examples bölümü iyi bir kaynaktır.
Veri değiştiren sorgularda EXPLAIN ANALYZE
BEGIN;
EXPLAIN ANALYZE UPDATE siparis SET tutar = tutar * 1.1 WHERE musteri_id = 7;
ROLLBACK;BEGIN
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------
Update on siparis (cost=4.37..40.79 rows=0 width=0) (actual time=0.529..0.530 rows=0.00 loops=1)
Buffers: shared hit=127 dirtied=2 written=1
-> Bitmap Heap Scan on siparis (cost=4.37..40.79 rows=10 width=22) (actual time=0.200..0.212 rows=10.00 loops=1)
Recheck Cond: (musteri_id = 7)
Heap Blocks: exact=10
Buffers: shared hit=12
-> Bitmap Index Scan on siparis_musteri_idx (cost=0.00..4.37 rows=10 width=0) (actual time=0.005..0.005 rows=10.00 loops=1)
Index Cond: (musteri_id = 7)
Index Searches: 1
Buffers: shared hit=2
Planning Time: 0.061 ms
Execution Time: 0.565 ms
(12 rows)
ROLLBACKDeğişikliği yapan iş en üstteki Update düğümündedir; altındaki düğümler güncellenecek satırları bulur. Update düğümünün süresi (0.53 ms) satırları bulmaktan uzun sürdü, ama planlayıcı bu iş için maliyet eklemez; kılavuza göre her doğru plan için aynı iş yapılacağından plan seçimini etkilemez. Satırlar güncellendiği için dirtied ve written sayıları da göründü. Transaction ve geri alma ayrıntıları için transaction yazısına bakabilirsiniz.
Plan seçimini sorgulamak: enable_* parametreleri
Planlayıcının neden bir yöntemi seçtiğini anlamak için alternatif planları zorlayabilirsiniz. Kılavuz enable_seqscan, enable_mergejoin gibi parametreleri bu amaçla "kaba ama faydalı bir araç" olarak tanımlar. Çoğu bu yöntemleri yasaklamaz, yalnızca caydırır; başka yol yoksa devre dışı düğüm yine kullanılır ve PostgreSQL 18'de çıktıda bu açıkça işaretlenir:
SET enable_seqscan = off;
EXPLAIN SELECT * FROM musteri WHERE sehir = 'Ankara';
RESET enable_seqscan; QUERY PLAN
-------------------------------------------------------------
Seq Scan on musteri (cost=0.00..199.00 rows=2000 width=25)
Disabled: true
Filter: (sehir = 'Ankara'::text)
(3 rows)sehir sütununda indeks olmadığı için sıralı tarama tek seçenek kaldı ve Disabled: true satırı bunu gösteriyor. Bu parametreleri yalnızca oturumunuzda deneme için kullanın; kalıcı ayar olarak bırakmak bütün sorguları etkiler.
Diğer EXPLAIN seçenekleri

| Seçenek | Ne ekler | Not |
|---|---|---|
| VERBOSE | Her düğümün çıktı sütunları, şema adlı tablo adları | Varsa sorgu kimliğini de gösterir |
| SETTINGS | Planlamayı etkileyen ve varsayılandan farklı ayarlar | Plan paylaşırken faydalı |
| GENERIC_PLAN | $1 gibi parametreli sorgu için genel plan | ANALYZE ile birlikte kullanılamaz |
| WAL | Üretilen WAL kayıt sayısı ve boyutu | Yalnızca ANALYZE ile |
| SERIALIZE | Çıktıyı metne veya ikiliye çevirme maliyeti | Yalnızca ANALYZE ile; veri istemciye yine gönderilmez |
| MEMORY | Planlama aşamasının bellek kullanımı | Planlayıcıyı incelemek için |
| FORMAT | TEXT, XML, JSON veya YAML çıktı | Araçlarla işlemek için JSON uygundur |
Makineyle işlenecek çıktılar için FORMAT JSON kullanılabilir. Aynı bilgi anahtar ve değer çiftleri olarak gelir:
EXPLAIN (FORMAT JSON, COSTS OFF) SELECT * FROM siparis WHERE id = 42; QUERY PLAN
-------------------------------------
[ +
{ +
"Plan": { +
"Node Type": "Index Scan", +
"Parallel Aware": false, +
"Async Capable": false, +
"Scan Direction": "Forward", +
"Index Name": "siparis_pkey",+
"Relation Name": "siparis", +
"Alias": "siparis", +
"Disabled": false, +
"Index Cond": "(id = 42)" +
} +
} +
]
(1 row)Bölümlenmiş tablolarda planda hangi bölümlerin elendiğini, Subplans Removed satırını ve Append düğümünü tablo bölümleme yazısında gerçek örnekle görebilirsiniz.
Plan okurken sık yapılan hatalar
- Maliyeti milisaniye sanmak:
costkeyfi birimdir; gerçek süre yalnızcaANALYZEçıktısındakiactual timedeğeridir. - loops değerini unutmak: Bir iç düğüm 1000 kez çalıştıysa gösterilen süre tek çalıştırmanın ortalamasıdır; toplam süre için çarpmak gerekir.
- Küçük test tablosundan genelleme yapmak: Kılavuz, maliyetlerin doğrusal olmadığını ve tek sayfalık bir tabloda indeks olsa bile neredeyse her zaman sıralı tarama görüleceğini hatırlatır. Planı gerçeğe yakın veri hacmiyle inceleyin.
- Ölçüm yükünü göz ardı etmek:
EXPLAIN ANALYZEher düğüm için zaman ölçtüğü için sorgu normalden belirgin şekilde yavaş çalışabilir; ağ aktarımı ve (SERIALIZE verilmezse) çıktı dönüşümü ise süreye dahil değildir. - Veri değiştiren sorguyu çıplak analiz etmek:
BEGINveROLLBACKarasına almayı unutmayın. - Tahmin ile gerçek arasındaki farka bakmamak: Plan sorunlarının çoğu, bir düğümde tahmin edilen ve gerçekleşen satır sayısının çok farklı olmasıyla başlar.
Sık Sorulan Sorular
EXPLAIN ile EXPLAIN ANALYZE arasındaki fark nedir?
EXPLAIN sorguyu çalıştırmaz, yalnızca planı ve tahminleri gösterir. EXPLAIN ANALYZE sorguyu çalıştırır ve her düğümün gerçek süresini, satır sayısını ve PostgreSQL 18'de arabellek kullanımını ekler.
Planlayıcı indeksim varken neden Seq Scan seçiyor?
Sorgu tablonun büyük bir kısmını döndürecekse sıralı okuma, dağınık indeks erişiminden ucuz olabilir. Tahminin doğru olup olmadığını EXPLAIN ANALYZE ile kontrol edin; istatistikler eskiyse önce ANALYZE çalıştırın.
Buffers satırındaki hit ve read ne anlama gelir?
hit, bloğun PostgreSQL'in paylaşılan arabelleğinde bulunduğunu ve okumaya gerek kalmadığını gösterir. read, bloğun arabellekte olmadığı için okunması gerektiğini belirtir. dirtied ve written ise sorgunun değiştirdiği ve arabellekten çıkarırken yazdığı blokları sayar.
EXPLAIN çıktısını bir araca nasıl aktarırım?
FORMAT JSON, XML veya YAML kullanın. Metin çıktısıyla aynı bilgiyi programların ayrıştırabileceği biçimde verir.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: PostgreSQL öğrenme rehberi · PostgreSQL performans optimizasyonu · İndeks türleri · VACUUM ve ANALYZE · SQL sorgu optimizasyonu