PostgreSQL'de tablo bölümleme (partitioning), mantıksal olarak tek olan büyük bir tabloyu daha küçük fiziksel parçalara ayırmaktır. Üst tabloyu PARTITION BY RANGE, LIST veya HASH ile tanımlar, bölümleri CREATE TABLE ... PARTITION OF ile eklersiniz. Eklenen her satır bölüm anahtarına göre doğru bölüme yönlendirilir; sorgularda ise planlayıcı, koşulla eşleşemeyecek bölümleri plandan çıkarır (partition pruning). Eski veriyi silmek de tek bir bölümü ayırıp kaldırmak kadar hızlı hâle gelir.
Bu yazıda üç bölümleme yöntemini, varsayılan (DEFAULT) bölümü, birincil anahtar kısıtlamasını, ATTACH ve DETACH ile bölüm bakımını ve pruning'in planlama ve yürütme aşamalarındaki etkisini PostgreSQL 18.1 üzerinde çalıştırılmış örneklerle anlatıyoruz. Plan çıktılarını okumaya yabancıysanız önce EXPLAIN ANALYZE yazısına göz atabilirsiniz.
Bölümleme ne zaman işe yarar?

Kılavuz bölümlemenin faydalarını şöyle sıralar: sık erişilen satırlar tek ya da birkaç bölümde toplandığında sorgu performansı belirgin şekilde artabilir; bir bölümün büyük kısmına erişen sorgular, bütün tabloya dağılmış rastgele indeks okumaları yerine o bölümü sırayla tarayabilir; toplu yükleme ve silme işlemleri bölüm ekleyip çıkararak yapılabilir ve DROP TABLE ya da DETACH PARTITION, toplu bir DELETE'ten çok daha hızlıdır ve onun yarattığı vacuum yükünü tamamen ortadan kaldırır; seyrek kullanılan veri daha ucuz depolama birimlerine taşınabilir.
Kılavuz bu faydaların ancak tablo aksi takdirde çok büyük olacaksa kayda değer olduğunu da belirtir. Verdiği kaba kural, tablonun boyutunun veritabanı sunucusunun fiziksel belleğini aşmasıdır. Küçük bir tabloyu bölümlemek planlama yükü ekler ama kazanç getirmez. Silme işinin vacuum'a yükü VACUUM ve bloat yazısında gösteriliyor.
| Yöntem | Bölüm tanımı | Uygun olduğu durum |
|---|---|---|
| RANGE | FOR VALUES FROM (alt) TO (üst); alt sınır dahil, üst sınır hariç | Tarih aralıkları, artan kimlik aralıkları |
| LIST | FOR VALUES IN (değer, ...); NULL için ayrı bir liste bölümü olabilir | Bölge, ülke, durum gibi ayrık değerler |
| HASH | FOR VALUES WITH (MODULUS m, REMAINDER r) | Doğal bir aralık veya liste olmadığında yükü eşit dağıtmak |
| DEFAULT | PARTITION OF ... DEFAULT | Hiçbir bölüme uymayan satırlar; HASH ile kullanılamaz |
RANGE ile aylık satış tablosu
Üst tablo kendi verisini tutmaz; kılavuzun ifadesiyle "sanal" bir tablodur ve depolama bölümlere aittir. Aylık bölümlerle bir satış tablosu kuralım:
CREATE TABLE satis (
id bigint GENERATED ALWAYS AS IDENTITY,
tarih date NOT NULL,
il text NOT NULL,
tutar numeric(10,2) NOT NULL
) PARTITION BY RANGE (tarih);
CREATE TABLE satis_2026_01 PARTITION OF satis FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE satis_2026_02 PARTITION OF satis FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
CREATE TABLE satis_2026_03 PARTITION OF satis FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
INSERT INTO satis (tarih, il, tutar) VALUES
('2026-01-15', 'İstanbul', 120.00),
('2026-02-01', 'Ankara', 80.50),
('2026-03-31', 'İzmir', 300.00);
SELECT tableoid::regclass AS bolum, tarih, il FROM satis ORDER BY tarih; bolum | tarih | il
---------------+------------+----------
satis_2026_01 | 2026-01-15 | İstanbul
satis_2026_02 | 2026-02-01 | Ankara
satis_2026_03 | 2026-03-31 | İzmir
(3 rows)tableoid sistem sütunu, her satırın fiziksel olarak hangi bölümde durduğunu gösterir. 1 Şubat tarihli satır Şubat bölümüne gitti: aralık bölümlerinde alt sınır dahil, üst sınır hariçtir, bu yüzden iki komşu bölüm aynı sınır değerini paylaşabilir. Kılavuz bölüm sınırlarının çakışmasına izin verilmediğini, çakışan bir bölüm tanımının hata vereceğini de belirtir. Bölüm sınırı kısıtlarını elle yazmanız gerekmez; sistem onları kendisi oluşturur.
Hiçbir bölüme uymayan satır ve DEFAULT bölüm
Nisan için bölüm yokken Nisan tarihli bir satış eklemeye çalışalım, ardından bir varsayılan bölüm oluşturup tekrar deneyelim:
INSERT INTO satis (tarih, il, tutar) VALUES ('2026-04-02', 'Bursa', 50);
CREATE TABLE satis_diger PARTITION OF satis DEFAULT;
INSERT INTO satis (tarih, il, tutar) VALUES ('2026-04-02', 'Bursa', 50);
CREATE TABLE satis_2026_04 PARTITION OF satis FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');ERROR: no partition of relation "satis" found for row
DETAIL: Partition key of the failing row contains (tarih) = (2026-04-02).
CREATE TABLE
INSERT 0 1
ERROR: updated partition constraint for default partition "satis_diger" would be violated by some rowVarsayılan bölüm olmadan uygun bölüm bulunamayan satır reddedildi. DEFAULT bölüm eklenince satır oraya gitti. Fakat bu kez Nisan bölümünü oluşturamadık: yeni bir bölüm eklendiğinde varsayılan bölüm, yeni bölüme ait olması gereken satır içermediğinden emin olmak için taranır ve bizim Nisan satırımız tam olarak orada duruyor. Çözüm, varsayılan bölümü ayırmak, yeni bölümü oluşturmak, satırları taşımak ve varsayılan bölümü geri bağlamaktır. PostgreSQL'de bu işlemlerin hepsi tek bir transaction içinde yapılabilir:
BEGIN;
ALTER TABLE satis DETACH PARTITION satis_diger;
CREATE TABLE satis_2026_04 PARTITION OF satis FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');
INSERT INTO satis (id, tarih, il, tutar) OVERRIDING SYSTEM VALUE
SELECT id, tarih, il, tutar FROM satis_diger WHERE tarih >= '2026-04-01' AND tarih < '2026-05-01';
DELETE FROM satis_diger WHERE tarih >= '2026-04-01' AND tarih < '2026-05-01';
ALTER TABLE satis ATTACH PARTITION satis_diger DEFAULT;
COMMIT;
SELECT tableoid::regclass AS bolum, count(*) FROM satis GROUP BY 1 ORDER BY 1;BEGIN
ALTER TABLE
CREATE TABLE
INSERT 0 1
DELETE 1
ALTER TABLE
COMMIT
bolum | count
---------------+-------
satis_2026_01 | 1
satis_2026_02 | 1
satis_2026_03 | 1
satis_2026_04 | 1
(4 rows)id sütunu GENERATED ALWAYS olduğu için mevcut değeri korumak üzere OVERRIDING SYSTEM VALUE kullandık. Bu deneyim, varsayılan bölümün bir emniyet ağı olduğunu ama büyüdükçe yeni bölüm eklemeyi pahalılaştırdığını gösterir: kılavuza göre tarama, varsayılan bölümde ACCESS EXCLUSIVE kilidi tutulurken yapılır. Kılavuzun önerisi, varsayılan bölüme eklenecek bölümün aralığını dışlayan bir CHECK kısıtı koyarak bu taramadan kaçınmaktır. En temiz yol ise bölümleri ihtiyaç doğmadan önceden oluşturmaktır; kılavuz bunun için gerekli DDL'i üreten bir betik yazmayı önerir.
Birincil anahtar ve benzersizlik kısıtı

Bölümlenmiş tablolarda en sık karşılaşılan sınırlama budur:
ALTER TABLE satis ADD PRIMARY KEY (id);
ALTER TABLE satis ADD PRIMARY KEY (id, tarih);ERROR: unique constraint on partitioned table must include all partitioning columns
DETAIL: PRIMARY KEY constraint on table "satis" lacks column "tarih" which is part of the partition key.
ALTER TABLEKılavuz nedeni açıklar: kısıtı oluşturan her bölüm indeksi benzersizliği yalnızca kendi bölümü içinde sağlayabilir. Farklı bölümlerde aynı anahtarın bulunmayacağını ancak bölüm yapısı garanti edebilir; bunun için kısıtın bütün bölüm anahtarı sütunlarını içermesi ve bölüm anahtarının ifade ya da fonksiyon çağrısı içermemesi gerekir. Aynı kural exclusion kısıtları için de geçerlidir ve bu kısıtlar bölüm anahtarı sütunlarını eşitlikle karşılaştırmalıdır. Bu yüzden birincil anahtarı (id, tarih) yaptık. Kısıtların genel kullanımı için PRIMARY KEY ve FOREIGN KEY yazısına bakabilirsiniz.
Diğer önemli sınırlamalar şunlardır:
- Normal bir tablo bölümlenmiş tabloya ya da tersi dönüştürülemez. Ancak mevcut bir tablo bölüm olarak bağlanabilir ve bir bölüm ayrılarak bağımsız tabloya dönüştürülebilir.
- Bölümler üst tabloda olmayan sütunlara sahip olamaz; bağlanacak tablonun sütunları üst tabloyla birebir eşleşmelidir.
- Üst tablodaki
CHECKveNOT NULLkısıtları bütün bölümlere kalıtılır; bu türlerdeNO INHERITkısıt oluşturulamaz. INSERTiçinBEFORE ROWtetikleyicileri satırın gideceği bölümü değiştiremez.- Geçici ve kalıcı tablolar aynı bölüm ağacında karıştırılamaz.
- Üst tablo veri tutmadığı için
TRUNCATE ONLYbölümlenmiş tabloda her zaman hata verir.
ERROR: cannot truncate only a partitioned table
HINT: Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.LIST ve HASH bölümleme
Liste bölümlemede her bölüm kabul ettiği değerleri açıkça sayar. NULL değerler için bir liste bölümü ayırabilirsiniz; aralık bölümlerinde ise NULL kullanılamaz. Bölüm anahtarı güncellenen bir satır, kendi bölümünün sınırına artık uymuyorsa başka bölüme taşınır:
CREATE TABLE musteri (id int, ad text, bolge text) PARTITION BY LIST (bolge);
CREATE TABLE musteri_marmara PARTITION OF musteri FOR VALUES IN ('İstanbul', 'Bursa', 'Kocaeli');
CREATE TABLE musteri_ege PARTITION OF musteri FOR VALUES IN ('İzmir', 'Aydın', 'Muğla');
CREATE TABLE musteri_bos PARTITION OF musteri FOR VALUES IN (NULL);
INSERT INTO musteri VALUES (1, 'Ali', 'Bursa'), (2, 'Ece', 'Muğla'), (3, 'Oya', NULL);
UPDATE musteri SET bolge = 'İzmir' WHERE id = 1;
SELECT tableoid::regclass AS bolum, * FROM musteri ORDER BY id; bolum | id | ad | bolge
-------------+----+-----+-------
musteri_ege | 1 | Ali | İzmir
musteri_ege | 2 | Ece | Muğla
musteri_bos | 3 | Oya |
(3 rows)Ali'nin satırı güncellemeyle Marmara bölümünden Ege bölümüne taşındı; kılavuzun belirttiği gibi bölüm anahtarı değişen satır, artık kendi bölümünün sınırına uymadığında başka bölüme aktarılır. Liste değerleri birebir eşleşme ile çalıştığı için "izmir" ile "İzmir" farklı değerlerdir; uygulamanın değeri hep aynı yazımla göndermesi gerekir. Türkçe büyük ve küçük harf davranışı için collation ve Türkçe sıralama yazısına bakabilirsiniz.
Hash bölümlemede her bölüm bir mod ve kalan değeriyle tanımlanır; satır, bölüm anahtarının hash değeri moda bölündüğünde verdiği kalana göre yerleşir:
CREATE TABLE oturum (kullanici_id int, baslangic timestamptz) PARTITION BY HASH (kullanici_id);
CREATE TABLE oturum_p0 PARTITION OF oturum FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE oturum_p1 PARTITION OF oturum FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE oturum_p2 PARTITION OF oturum FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE oturum_p3 PARTITION OF oturum FOR VALUES WITH (MODULUS 4, REMAINDER 3);
INSERT INTO oturum SELECT i, now() FROM generate_series(1, 1000) i;
SELECT tableoid::regclass AS bolum, count(*) FROM oturum GROUP BY 1 ORDER BY 1;
CREATE TABLE oturum_varsayilan PARTITION OF oturum DEFAULT; bolum | count
-----------+-------
oturum_p0 | 259
oturum_p1 | 234
oturum_p2 | 276
oturum_p3 | 231
(4 rows)
ERROR: a hash-partitioned table may not have a default partition1.000 kullanıcı dört bölüme yaklaşık eşit dağıldı ve hash bölümlemede varsayılan bölüm kullanılamadığı doğrulandı. Kılavuz, bölüm sayısının ileride nasıl değişebileceğini düşünmeyi önerir: bugün az sayıda büyük müşteri için müşteri başına LIST bölümü mantıklı görünse de birkaç yıl sonra çok sayıda küçük müşteri olabilir; böyle durumlarda makul sayıda bölümle HASH seçmek daha iyi olabilir.
Partition pruning: gereksiz bölümleri atlamak
Bölümlemenin sorgu tarafındaki kazancı pruning'dir. Planlayıcı her bölümün sınırlarına bakar ve WHERE koşulunu sağlayan satır içeremeyeceğini kanıtladığı bölümleri plandan çıkarır. Tabloya 30.000 satır ekleyip istatistik topladıktan sonra Mart ve sonrası için bir sayım yapalım, ardından pruning'i kapatıp aynı sorguya bakalım:
INSERT INTO satis (tarih, il, tutar)
SELECT DATE '2026-01-01' + (i % 120), (ARRAY['İstanbul','Ankara','İzmir'])[i % 3 + 1], i % 1000
FROM generate_series(1, 30000) i;
ANALYZE satis;
EXPLAIN (COSTS OFF) SELECT count(*) FROM satis WHERE tarih >= '2026-03-01';
SET enable_partition_pruning = off;
EXPLAIN (COSTS OFF) SELECT count(*) FROM satis WHERE tarih >= '2026-03-01';
RESET enable_partition_pruning; QUERY PLAN
-----------------------------------------------------
Aggregate
-> Append
-> Seq Scan on satis_2026_03 satis_1
Filter: (tarih >= '2026-03-01'::date)
-> Seq Scan on satis_2026_04 satis_2
Filter: (tarih >= '2026-03-01'::date)
-> Seq Scan on satis_diger satis_3
Filter: (tarih >= '2026-03-01'::date)
(8 rows)
QUERY PLAN
-----------------------------------------------------
Aggregate
-> Append
-> Seq Scan on satis_2026_01 satis_1
Filter: (tarih >= '2026-03-01'::date)
-> Seq Scan on satis_2026_02 satis_2
Filter: (tarih >= '2026-03-01'::date)
-> Seq Scan on satis_2026_03 satis_3
Filter: (tarih >= '2026-03-01'::date)
-> Seq Scan on satis_2026_04 satis_4
Filter: (tarih >= '2026-03-01'::date)
-> Seq Scan on satis_diger satis_5
Filter: (tarih >= '2026-03-01'::date)
(12 rows)Pruning açıkken Ocak ve Şubat bölümleri plana hiç girmedi; Append düğümü yalnızca üç bölümün sonuçlarını birleştiriyor. Varsayılan bölüm ise her zaman aday kalıyor, çünkü tanımı gereği her değeri içerebilir. Pruning'i kapattığımızda beş bölümün hepsi tarandı. Kılavuz enable_partition_pruning parametresinin postgresql.conf içinde kapatılmadığından emin olmanızı ister.
Pruning yalnızca bölüm anahtarı üzerindeki koşullarla çalışır; indekslerin varlığıyla ilgisi yoktur. Bölüm anahtarı olmayan bir sütunla filtrelediğinizde, örneğin WHERE il = 'Ankara', planda yine beş bölümün hepsi görünür. Bu yüzden kılavuzun en kritik tasarım önerisi, en sık WHERE koşullarında geçen sütunu ya da sütunları bölüm anahtarı yapmaktır.
Yürütme sırasında pruning
Koşuldaki değer planlama anında bilinmiyorsa, örneğin hazırlanmış bir sorgunun parametresi, bir alt sorgunun sonucu veya iç içe döngüdeki dış satır değeri ise pruning yürütme sırasında yapılabilir. Genel bir plan kullanılmasını zorlayarak bunu görebiliriz:
SET plan_cache_mode = force_generic_plan;
PREPARE ay_toplam(date) AS SELECT sum(tutar) FROM satis WHERE tarih = $1;
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, BUFFERS OFF, SUMMARY OFF) EXECUTE ay_toplam('2026-02-10'); QUERY PLAN
----------------------------------------------------------------------------
Aggregate (actual rows=1.00 loops=1)
-> Append (actual rows=250.00 loops=1)
Subplans Removed: 4
-> Seq Scan on satis_2026_02 satis_1 (actual rows=250.00 loops=1)
Filter: (tarih = $1)
Rows Removed by Filter: 6751
(6 rows)Plan $1 parametresiyle genel olarak kuruldu, ama yürütme başlarken değer belli olduğu için dört bölüm çıkarıldı ve Subplans Removed: 4 satırı bunu raporladı. Kılavuz, bu aşamada çıkarılan bölümlerin yürütme başında yine de kilitlendiğini not eder. Değerin yürütme boyunca değiştiği durumlarda (örneğin iç içe döngüler) hangi bölümün atlandığını loops değerlerinden ve (never executed) notundan anlamak gerekir.
Bölüm bakımı: indeks, DETACH, DROP ve ATTACH

Üst tabloda oluşturulan bir indeks, mevcut bütün bölümlerde ve sonradan eklenecek bölümlerde eşleşen bir indeks oluşturur:
CREATE INDEX ON satis (il);
SELECT tablename, indexname FROM pg_indexes
WHERE tablename LIKE 'satis%' AND indexname LIKE '%il%' ORDER BY 1; tablename | indexname
---------------+----------------------
satis | satis_il_idx
satis_2026_01 | satis_2026_01_il_idx
satis_2026_02 | satis_2026_02_il_idx
satis_2026_03 | satis_2026_03_il_idx
satis_2026_04 | satis_2026_04_il_idx
satis_diger | satis_diger_il_idx
(6 rows)Bölümlenmiş tabloda indeks oluştururken CONCURRENTLY kullanılamaz. Uzun kilitten kaçınmak için kılavuz şu yolu önerir: üst tabloda CREATE INDEX ON ONLY ile geçersiz işaretli bir indeks oluşturmak, her bölümde CREATE INDEX CONCURRENTLY çalıştırmak ve bunları ALTER INDEX ... ATTACH PARTITION ile üst indekse bağlamak. Bütün bölümler bağlandığında üst indeks kendiliğinden geçerli olur. İndeks türleri için indeks yazısına bakabilirsiniz.
Eski veriyi silmenin en hızlı yolu bölümü kaldırmaktır. Önce ayırıp veriyi yedekleme veya özetleme fırsatı bulabilir, sonra silebilirsiniz. Yeni ayı ise önce bağımsız bir tablo olarak hazırlayıp sonra bağlamak, veriyi yüklemeden ve kontrol etmeden önce tabloya görünür olmasını engeller:
ALTER TABLE satis DETACH PARTITION satis_2026_01;
SELECT count(*) FROM satis_2026_01; -- sonuç: 7751 (artık bağımsız tablo)
DROP TABLE satis_2026_01;
CREATE TABLE satis_2026_05 (LIKE satis INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
ALTER TABLE satis_2026_05 ADD CONSTRAINT satis_2026_05_aralik
CHECK (tarih >= DATE '2026-05-01' AND tarih < DATE '2026-06-01');
INSERT INTO satis_2026_05 (id, tarih, il, tutar) VALUES (900001, '2026-05-03', 'Ankara', 75);
ALTER TABLE satis ATTACH PARTITION satis_2026_05 FOR VALUES FROM ('2026-05-01') TO ('2026-06-01');
ALTER TABLE satis_2026_05 DROP CONSTRAINT satis_2026_05_aralik;
SELECT relid, isleaf FROM pg_partition_tree('satis'); relid | isleaf
---------------+--------
satis | f
satis_2026_02 | t
satis_2026_03 | t
satis_diger | t
satis_2026_04 | t
satis_2026_05 | t
(6 rows)Bağlamadan önce eklediğimiz CHECK kısıtı, kılavuzun önerdiği bir tekniktir: ATTACH PARTITION normalde bölüm kısıtını doğrulamak için tabloyu, ACCESS EXCLUSIVE kilidi tutarken tarar. Eşleşen bir CHECK varsa bu tarama atlanır; bağlama bittikten sonra artık gereksiz olan kısıtı kaldırmak önerilir. Kilit seviyeleri de önemlidir:
| İşlem | Üst tablodaki kilit | Not |
|---|---|---|
| CREATE TABLE ... PARTITION OF | ACCESS EXCLUSIVE | Üst tabloya erişimi kısa süre durdurur |
| ALTER TABLE ... ATTACH PARTITION | SHARE UPDATE EXCLUSIVE | Bağlanan tablo ve varsayılan bölümde ACCESS EXCLUSIVE alınır |
| ALTER TABLE ... DETACH PARTITION | ACCESS EXCLUSIVE | Bölüm bağımsız tablo olarak kalır |
| DETACH PARTITION ... CONCURRENTLY | SHARE UPDATE EXCLUSIVE | Transaction bloğunda çalışmaz; varsayılan bölüm varsa kullanılamaz |
| DROP TABLE bölüm | ACCESS EXCLUSIVE | Milyonlarca satırı tek tek silmeden kaldırır |
Örneğimizde satis tablosunun varsayılan bölümü olduğu için DETACH ... CONCURRENTLY kullanılamazdı. Kilit kiplerinin birbirini nasıl beklettiği kilitler yazısında anlatılıyor. Bölümlerin listesini pg_partition_tree fonksiyonu veya psql'in \d+ satis komutu gösterir.
İstatistikler ve vacuum
Autovacuum bölümleri normal tablo gibi temizler ve analiz eder, ama veri tutmayan üst tabloyu işlemez. Planlayıcının üst tablo düzeyinde istatistiğe ihtiyaç duyduğu sorgular için kılavuz, üst tabloyu ilk doldurulduğunda ve bölümlerdeki dağılım belirgin şekilde değiştiğinde elle analiz etmeyi önerir. PostgreSQL 18'de ANALYZE ve VACUUM için gelen ONLY seçeneği, bölümlenmiş tabloyu çocuklarını yeniden işlemeden ele almayı sağlar:
ANALYZE ONLY satis; -- yalnızca üst tablonun istatistiği; sonuç: ANALYZEİyi tasarım için öneriler
- Bölüm anahtarını sorgulara göre seçin: Pruning yalnızca bölüm anahtarındaki koşullarla çalışır. Birincil anahtar zorunluluğu ve eski verinin silinme biçimi de karara dahil edilmelidir.
- Bölüm sayısını abartmayın: Kılavuza göre planlayıcı birkaç bin bölümü, tipik sorgular bölümlerin çoğunu eleyebildiği sürece iyi yönetir. Elenmeyen bölüm arttıkça planlama süresi ve bellek kullanımı artar; her oturum dokunduğu bölümlerin meta verisini kendi belleğine yükler.
- Az bölüm de sorundur: Çok az bölüm, indekslerin büyük kalması ve önbellek isabetinin düşmesi anlamına gelebilir.
- İş yüküne göre karar verin: Veri ambarı tipi yüklerde daha çok bölüm makul olabilir, OLTP yüklerinde planlama süresi daha belirleyicidir. Kılavuz, çok bölümün az bölümden ya da tersinin her zaman daha iyi olduğunu varsaymamanızı söyler.
- Erken karar verin: Büyük veriyi sonradan yeniden bölümlemek çok yavaş olabilir; iş yükünü önceden simüle etmek faydalıdır.
- Gelecek bölümleri önceden oluşturun: Varsayılan bölüme veri birikmesini ve yeni bölüm eklerken taramayı önler.
PostgreSQL 18 ve 18.x notları
PostgreSQL 18 sürüm notlarında bölümlemeyle ilgili maddeler şunlardır: çok sayıda bölüme erişen sorguların planlanması daha verimli hâle getirildi, partitionwise join daha fazla durumda kullanılabiliyor ve bellek kullanımı azaltıldı, bölüm sorgularının maliyet tahminleri iyileştirildi, B-tree dışındaki benzersiz indeksler bölüm anahtarı olarak kullanılabiliyor, bölümlenmiş tablolarda NOT VALID yabancı anahtar kısıtına izin veriliyor ve kısıtlar ONLY ile yalnızca üst tablodan kaldırılabiliyor. Uyumluluk açısından önemli bir değişiklik de var: UNLOGGED bölümlenmiş tablo oluşturmaya artık izin verilmiyor.
Bakım sürümleri de önemli düzeltmeler içeriyor. 18.1 notlarına göre üretilmiş (generated) sütunlar artık bölüm anahtarında kullanılamıyor. 18.6 notları ise RANGE ile bölümlenmiş tablolarda pruning hatasını düzeltiyor: bazı durumlarda varsayılan bölüm atlanmaması gerekirken atlanıyor ve sorgu sonucundan satırlar eksik kalabiliyordu. Bu yazıdaki örnekler 18.1 üzerinde çalıştırıldı; varsayılan bölüm kullanıyorsanız en güncel 18.x bakım sürümüne geçmeniz özellikle önemlidir. Sürüm yükseltme için pg_upgrade yazısına bakabilirsiniz.
Sık Sorulan Sorular
Mevcut büyük bir tabloyu bölümlenmiş tabloya çevirebilir miyim?
Doğrudan çeviremezsiniz; kılavuz normal tablonun bölümlenmiş tabloya dönüştürülemeyeceğini belirtir. Yeni bir bölümlenmiş tablo oluşturup mevcut tabloyu uygun sınırlarla ATTACH PARTITION ile bölüm olarak bağlayabilir veya veriyi yeni yapıya kopyalayabilirsiniz.
Bölüm anahtarı olmayan sütunda benzersizlik sağlayabilir miyim?
Bölümlenmiş tablo düzeyinde hayır; benzersiz ve birincil anahtar kısıtları bütün bölüm anahtarı sütunlarını içermelidir. Tek tek bölümlerde kendi benzersiz indekslerinizi oluşturabilirsiniz, ama bunlar bölümler arası tekrarı engellemez.
Partition pruning neden çalışmıyor?
Koşulun bölüm anahtarı üzerinde olduğundan, enable_partition_pruning parametresinin açık olduğundan ve EXPLAIN çıktısında bölümlerin elendiğinden emin olun. Değer yürütme sırasında belli oluyorsa Subplans Removed satırına bakın.
Kalıtım (inheritance) ile bölümleme kurmalı mıyım?
Kılavuz kalıtım tabanlı bölümlemeyi eski yöntem olarak ele alır ve yerleşik bildirimsel bölümlemenin bazı performans avantajlarını sunmadığını belirtir. Yerleşik yöntemlerin karşılamadığı özel bir ihtiyaç yoksa bildirimsel bölümleme tercih edilmelidir.
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 · EXPLAIN ANALYZE ile plan okuma · İndeks türleri · VACUUM ve autovacuum · CREATE TABLE ve ALTER TABLE