PostgreSQL'de büyük tabloyu bölümleme: yöntem seçimi, kurulum ve bakım
Beceri rehberi

PostgreSQL'de büyük tabloyu bölümleme: yöntem seçimi, kurulum ve bakım

Hazırlayan: Mindset Institute EditörlüğüGüncellendi: 5 dk okuma

PostgreSQL'de büyük tabloyu bölümleme: aralık, liste ve hash yöntemleri, SQL ile kurulum, partition pruning, eski veriyi hızla silme ve kısıtlamalar.

Bölümleme ne zaman anlamlıdır?

Bölümleme (partitioning), mantıksal olarak tek görünen büyük bir tabloyu fiziksel olarak daha küçük parçalara ayırmaktır. PostgreSQL belgelerine göre bölümlenmiş tablonun kendi depolama alanı yoktur; satırlar bölüm anahtarına göre alt tablolara (bölümlere) yönlendirilir. Uygulama yine tek tabloyla konuşur.

Bölümleme her yavaş sorgunun ilacı değildir. Doğru gerekçeler şunlardır: • Sorgular ağırlıklı olarak bölüm anahtarına göre filtreliyorsa, planlayıcı ilgisiz bölümleri hiç taramaz (partition pruning). • Eski veriyi toplu silmek gerekiyorsa, çok sayıda satırı tek tek silmek yerine bir bölümü tablo olarak ayırıp düşürmek çok daha hızlı ve az bakım yükü gerektirir. • Tablo ve indeksleri bellekten büyükse, sıcak veriyi küçük bölümlerde tutmak önbellek verimini artırır. • Bakım işleri (vacuum, indeks yeniden oluşturma) bölüm bazında yapılabilir.

Bölümleme gereksiz olduğu durumlar da vardır: tablo küçükse, sorgular bölüm anahtarını hiç kullanmıyorsa ya da doğru bir indeks sorunu zaten çözüyorsa karmaşıklığı artırmaktan başka bir işe yaramaz. Önce yavaş sorguyu EXPLAIN ile inceleyin, indeks seçeneğini tüketin, sonra bölümlemeye karar verin.

Yöntemler: aralık, liste ve hash

PostgreSQL'in bildirimsel (declarative) bölümleme özelliği şu yöntemleri sunar.

Aralık (range): Anahtar değerler çakışmayan aralıklara ayrılır. Alt sınır dahil, üst sınır hariçtir. Tarih ve zamana dayalı veriler için en yaygın seçimdir: sipariş kayıtları, olay günlükleri, ölçüm verileri. Eski veriyi düşürmek bu yöntemle çok kolaydır.

Liste (list): Her bölüm, anahtarın açıkça sayılan değerlerini barındırır. Bölge, ülke, müşteri grubu gibi sınırlı ve sabit kategoriler için uygundur.

Hash: Anahtarın hash değeri, bir bölen ve kalan çifti ile bölümlere dağıtılır. Doğal bir aralık ya da liste yoksa ve yükü eşit dağıtmak istiyorsanız kullanılır. Belgeler, anahtar değer sayısının öngörülemez büyüyeceği durumlarda, makul bölüm sayısıyla hash'in listeye tercih edilebileceğini belirtir.

Yöntem seçerken şu soruyu sorun: Sorgularım hangi sütuna göre filtreliyor ve verinin ömrü nasıl yönetilecek? Zamana bağlı verilerde aralık, sabit kategoride liste, bunlar uygun değilse hash düşünülür.

Adım adım kurulum: SQL örneği

Kurgusal bir sipariş günlüğü tablosunu aylık aralıkla bölümleyelim. Önce ana tabloyu bölüm anahtarıyla tanımlarsınız:

CREATE TABLE siparis_log ( siparis_id bigint NOT NULL, olusma_tarihi date NOT NULL, tutar numeric ) PARTITION BY RANGE (olusma_tarihi);

Ardından her ay için bir bölüm eklersiniz:

CREATE TABLE siparis_log_ocak PARTITION OF siparis_log FOR VALUES FROM ('<ay başı>') TO ('<sonraki ay başı>');

Liste yönteminde bölüm şöyle tanımlanır:

CREATE TABLE customer_region_istanbul PARTITION OF customer_region FOR VALUES IN ('istanbul');

Hash yönteminde ise bölen ve kalan verilir:

CREATE TABLE olay_h_a PARTITION OF olay FOR VALUES WITH (MODULUS <bölüm sayısı>, REMAINDER <kalan>);

Üst tabloya indeks tanımlamak, mevcut ve gelecekteki tüm bölümlere indeks yaymak anlamına gelir:

CREATE INDEX ON siparis_log (olusma_tarihi);

Belgelere göre, hiçbir bölüme uymayan bir satır eklenirse hata alırsınız. Bu yüzden bölümleri veriden önce oluşturmak ya da bir varsayılan (DEFAULT) bölüm tanımlamak gerekir. Her yeni dönem için bölüm açma işini, zamanlanmış bir görevle veya bu iş için yazılmış bir araçla otomatikleştirin; elle yapılan bölüm açma işlemi sonunda unutulur.

Partition pruning: bölümlemenin asıl kazancı

Bölümlemeyle sorguların hızlanmasının nedeni, planlayıcının bölüm sınırlarına bakıp sorguyla ilgisiz bölümleri dışarıda bırakmasıdır. Buna partition pruning denir ve enable_partition_pruning ayarı varsayılan olarak açıktır. Belgelere göre pruning hem plan sırasında hem de çalışma sırasında (parametre, alt sorgu sonuçları gibi) yapılabilir ve indekslere değil bölüm sınırlarına dayanır.

Bir sorgunun pruning'den yararlandığını görmek için EXPLAIN kullanın:

EXPLAIN SELECT count(*) FROM siparis_log WHERE olusma_tarihi >= DATE '<belirli bir tarih>';

Planda yalnızca ilgili bölümlerin tarandığını görmelisiniz. Tüm bölümler tarandıysa, sorgu bölüm anahtarını doğrudan ve yalın biçimde kullanmıyor demektir. Anahtar sütun üzerinde fonksiyon uygulamak ya da tipi örtük dönüştürmek, pruning'i engelleyebilir.

Bu yüzden bölüm anahtarı, belgelerin de önerdiği gibi, WHERE koşullarında en sık kullanılan sütun olmalıdır. Anahtarı yanlış seçmek, bölümlemenin tüm maliyetini taşıyıp kazancı vermemesi demektir. Karar verirken gerçek sorgu kayıtlarına bakın, tahmine değil.

Veri yaşam döngüsü: ATTACH, DETACH ve eski veriyi silme

Bölümlemenin en pratik faydası, veri yaşam döngüsünü yönetmektir.

Eski veriyi atmak için en hızlı yol, bölümü düşürmektir:

DROP TABLE siparis_log_ocak;

Belgeler bunun en hızlı silme yolu olduğunu, ancak üst tablo üzerinde güçlü bir kilit gerektirdiğini belirtir. Veriyi saklamak istiyorsanız bölümü ayırıp bağımsız tabloya çevirirsiniz:

ALTER TABLE siparis_log DETACH PARTITION siparis_log_ocak;

Eşzamanlı çalışmayı daha az etkileyen CONCURRENTLY seçeneği de vardır. Böylece arşivleme, yedekleme ya da başka bir ortama taşıma ayrı yapılabilir.

Yeni veri yüklemek için önce ayrı bir tablo oluşturup veriyi oraya yükleyebilir, uygun bir CHECK kısıtı ekleyip sonra bölüm olarak bağlayabilirsiniz:

ALTER TABLE siparis_log ATTACH PARTITION siparis_log_yeni FOR VALUES FROM ('<ay başı>') TO ('<sonraki ay başı>');

Belgelere göre uygun CHECK kısıtı varsa PostgreSQL doğrulama taramasını atlayabilir; yoksa bölümü tarayarak doğrular. Varsayılan bölüm varsa yeni aralığın varsayılan bölümde veri içermediği de doğrulanır; bunu hızlandırmak için varsayılan bölüme uygun kısıt eklenebilir.

Bu yaklaşım, aylık veriyi dönem sonunda yüklemek, eski dönemleri arşive atmak gibi iş akışlarını kesintisiz hale getirir.

Kısıtlamalar ve bakım

Bölümlemenin getirdiği sınırları tasarımda baştan hesaba katın. PostgreSQL belgelerine göre: • Birincil anahtar ve benzersiz kısıtlar, bölüm anahtarındaki tüm sütunları içermek zorundadır. Yalnızca sipariş numarasını benzersiz yapmak istiyorsanız bu tek başına mümkün olmaz; tarih sütunuyla birlikte tanımlamanız gerekir. • Bölümler, üst tablodan farklı sütun taşıyamaz. • Satır öncesi tetikleyiciler hedef bölümü değiştiremez. • Üst tabloda indeks oluşturmada CONCURRENTLY kullanılamaz; bölüm bazında yapıp üst indekse bağlarsınız.

Bakım tarafında önemli bir uyarı: belgeler, otomatik vacuum'un bölümlenmiş üst tabloyu analiz etmediğini belirtir. Üst tabloya yönelik istatistikler gerekiyorsa ANALYZE'ı elle ya da zamanlanmış görevle çalıştırın. Bölümlerin kendisi normal tablolar gibi vacuum ve analizden geçer.

Bölüm sayısı da bir dengedir. Belgeler, çok az bölümün indeks boyutunu ve önbellek yerelliğini kötüleştirdiğini, çok fazla bölümün ise plan süresini ve bellek kullanımını artırdığını söyler. Baştan öngörülü olun: büyük veriyi sonradan yeniden bölümlemek yavaş ve zahmetlidir. Gerçekçi yük simülasyonuyla deneyin.

Bu beceriyi Mindset programlarında nerede çalışırsınız

"PostgreSQL İleri Seviye: Performans ve Yönetim" eğitiminin Bölümleme ve Büyük Tablo Yönetimi modülü bu sayfadaki konuyu uygulamalı olarak ele alır; Mimari, MVCC ve Vacuum Yönetimi modülü de bölümlenmiş tabloların bakımını anlamak için gerekli zemini hazırlar. Eğitim, büyük tablolarla çalışan veritabanı yöneticileri, arka uç geliştiricileri ve veri mühendisleri için uygundur.

İlgili Mindset programı

PostgreSQL İleri Seviye: Performans ve Yönetim

Sık sorulanlar

Sık sorulan sorular

Bölümleme sorguyu her zaman hızlandırır mı?

Hayır. Kazanç, sorgunun bölüm anahtarına göre filtrelemesine ve planlayıcının ilgisiz bölümleri atlayabilmesine bağlıdır. Anahtarı kullanmayan sorgular tüm bölümleri tarar ve plan maliyeti artar. Önce sorguyu EXPLAIN ile inceleyin, indeksle çözüp çözemeyeceğinizi sınayın, sonra bölümlemeyi düşünün.

Mevcut büyük bir tabloyu bölümlenmiş tabloya nasıl çevirirsiniz?

Mevcut tablo doğrudan bölümlenmiş hale getirilemez. Yaygın yol, bölümlenmiş yeni bir tablo oluşturmak, veriyi bölümlere taşımak ya da eski tabloyu bir bölüm olarak bağlamak ve uygulamayı geçirmektir. Büyük veride bu planlı bir bakım penceresi ve ayrıntılı test gerektirir.

Birincil anahtarı neden yalnızca kimlik sütunuyla tanımlayamıyorum?

Çünkü benzersizlik kısıtı, bölüm anahtarındaki tüm sütunları içermek zorundadır. PostgreSQL, bölümler arası benzersizliği ayrı ayrı bölüm indeksleriyle sağladığı için anahtar sütunlar kısıta dahil edilmelidir. Tasarımda bölüm anahtarını kimlikle birlikte birincil anahtara katın.

Varsayılan bölüm ne işe yarar?

Hiçbir tanımlı bölüme uymayan satırları yakalar; böylece ekleme hataya düşmez. Ancak varsayılan bölümde veri biriktiği fark edilmezse yeni bölüm eklemek zorlaşır, çünkü yeni aralığın varsayılan bölümde veri içermediği doğrulanmalıdır. Düzenli kontrol ve otomatik bölüm açma gerekir.

Eski veriyi silmek için DELETE mi, bölümü düşürmek mi daha iyi?

Bölümü düşürmek ya da ayırmak çok daha hızlı ve hafiftir; satır satır silme, ölü satır ve vacuum yükü doğurur. Veriyi saklamak gerekiyorsa önce DETACH ile ayırıp arşivleyin. Silme işlemini tablo kilidi gereksinimine göre bakım penceresine yerleştirin.

PostgreSQL Tablo Bölümleme (Partitioning) Nasıl Yapılır