PostgreSQL 11 Yenilikleri 3 - Partition Yenilikleri

Merhaba,

PostgreSQL 11’de partition’un nasıl yapıldığından bahsetmeden önce Partitioning nedir ve PostgreSQL 10 ve öncesinde nasıl yapıldığından bahsetmek isterim.

Verilerin belirlenen kriterlerde farklı tablolara ayrıştırılmasına Partitioning denir. PostgreSQL’de partitioned tabloları bireysel tabloların oluşturduğu partition tablolarından meydana gelir. Bu tablolar tek bir tablo altında gruplandırılırlar. 10GB tablonuz olsun. Tüm tablodan veri çekmek yerine daha küçük boyuttaki partition tablolarından veri çekmek daha hızlı olacaktır. Eğer sorgunuzun yapısı partition yapınıza uygunsa arayacağı kriterin olduğu partition tablosuna gidecektir. Örneğin tarihlere göre aylık olarak tablonuzu böldüyseniz, sorgunuzda partition key değeriniz yoksa işe yaramayacaktır. Önemli noktalardan biri partition key’inin kullanılmasıdır.

PostgreSQL 9.6 ve öncesinde partition yapısını kurmak için partitioned tabloya eklenecek verilerin partition tablolarına eklenmesi için trigger oluşturuyorduk. Tüm kriterleri trigger function içine ekliyorduk. PostgreSQL10 ile partition yapısı değiştirildi. Artık trigger kullanmadan partition yapısını kurabiliyoruz. Partition oluşturma işlemi büyük ölçüde manuellikten kurtarıldı diyebiliriz.

PostgreSQL 10 ile RANGE ve LIST partitioning özelliği geldi. RANGE Partitioning, tabloyu belirlediğimiz kriter için bölmeyi sağlar. Tabloyu id’lere göre bölmek isterseniz partition tablolarında vereceğimiz kriter id aralığı olmalıdır. LIST partition ile Partition Key değeri ne ise sadece ona ait veriler parçalanabilir. Bir partition tablosunda çok yük varken diğerinde daha az yük olabilir.

RANGE Partition Örneği




LIST Partition Örneği



Default Partition

Eğer partition kriterlerine uymayan bir veri eklenmesi söz konusu olursa partitioned tablonun default partition’una bu veri eklenecek. Burada dikkat edilmesi gereken Default partition eğer diğer partition tablolarından önce aratıldıysa önce default partition partitioned tablodan DETACH ile ayırt edilmeli ve veriler uygun partition tablolarına manuel olarak aktarılmalıdır. Sonrasında Default Partition partitioned tabloya bağlanabilir.

iki sütunlu bir tablo oluşturdum. 1-100 ve 100-200 arasındaki verileri students_p1 ve students_p2 tablolarına atacak. Biz 200 den büyük sayıları tabloya eklediğimizde hata vermemesi için DEFAULT Partition tablosu ekleyeceğiz.



Tablo yapısına uygun hangi veriyi eklersek ekleyelim hata vermeyecek.



1-200 arasındaki verileri p1 ve p2 partition tablolarına aktaracak. uygun partition bulamadığı verileri de DEFAULT partitiona aktaracak.



Default partition olmadığında son insert işleminde hata verecek. 


Burada küçük bir not eklemek gerekir. DEFAULT partitiıon HASH partition için oluşturulamaz. Çünkü HASH partition zaten verilerini aktaracağı tüm partition tablolarına sahiptir.

Partition Key güncellemesi

PostgreSQL 10 ve öncesinde Partition Key değerinin güncellenmesi sonucu eğer veri başka bir partition altına geçmesi mümkün değildi. PostgreSQL11 ile partitionlar arası verilerin güncellemesi mümkün.

Bir önceki örnekte tabloyu TRUNCATE edip, içine bir satır ekiyorum.



Eklediğim satır p1 partition tablosunda saklanıyor aslında.



id alanı benim partition key değerim. p1 için id alanı 1-100 arasında olmalı. ben bu değeri 190 olarak güncelleyeceğim. Güncelleme sonucunda veri p2 partittion tablosuna geçecek.


Otomatik Index oluşturulması

PostgreSQL10 ile partitioned tablolara sonradan eklenen indexleri aynı şekilde partition tablolarına manuel index eklemek zorunda kalıyorduk. PostgreSQL 11 ile index’in ana tabloda yaratılması yeterli. Her yeni eklenen partition tablosunun indexlerini postgres otomatik olarak eklendiği gibi mevcut partition tablolarına da indexler eklenecek. Mevcut partition tablolarında indexler otomatik olarak oluşturulduğunda tablo üzerindeki lock seviyesi ShareLock olur. Write ve Read işlemleri bu lock ile etkilenmez.

students tablosuna index ekleyelim.




Partition tablosuna baktığımızda index’in otomatik olarak oluştuğunu görebiliriz.


Foreign Key, Unique Key ve Trigger Desteği

PostgreSQL11 ile partition tablolarını Foreign Key ve Unique Key kısıtıyla birlikte oluşturabiliriz. Unique Key değerini PostgreSQL child tablolarda da otomatik olarak oluşturacaktır. 


Unique Key desteğinin partitioned tabloya gelmesi demek aynı zamanda partition tablolar arası verilerin unique olduğunun kanıtı olacak bizim için.

Aggregate için - enable_partitionwise_aggregate

Gruplama ve aggregation işlemlerinin partition tabloları için ayrı ayrı yapılmasını sağlar. Daha çok CPU ve memory gerektirir fakat her bir partition tablosu için aggregate yapacağı için daha sorgu performansını artıracaktır. Bu özellik default olarak off durumundadır. Default off durumundayken aggregation fonksiyonları için kullanılan execution plan aşağıdaki gibidir;




Explain Analyze ile sorgu planına baktığımızda dikkat etmemiz gereken iki anahtar yer bulunmakta; Append ve HashAggregate. Append; altında çalışan tüm sorguların sonucu tek bir result set gibi döndürür. HashAggreagete; Group By ve Aggregate fonksiyonları(count, sum.. gibi) birlikte kullanıldığında sorgu planına eklenir ve altındaki sorgunun tamamını gruplayarak duplicated verileri siler.

Dolayısıyla, yukarıda yapılan işlem şu; önce tüm partition tabloları tek tablo gibi birleştirilir ve daha sonra gruplanır. 

set enable_partitionwise_aggregate=on yaptığımızda her partition tablosu için ayrı ayrı aggregate yapacak. Yani yukarıdaki planın aksine önce her bir partition tablosu için gruplama yapar ve sonrasında tek bir result set olarak birleştirir.




HASH Partitioning

Partition Key’in ait hash değerine göre satır bazlı verilerin partition tablolarına aktarılmasıdır. RANGE ve LIST partitionuna benzer şekilde oluşturulur.




Partition tablolarının oluşturulması sırasında MODULUS ve REMAINDER kavramları geldi. MODULUS değeri bizim kaç adet partition tablomuzun olduğunu belirtir ve tüm partition tabloları için sabittri, değişmez. REMAINDER değeri HASH partition için kullanılan hash key'in, yani tablomuzu bölmek için kullanacağımız sütun, hash değerini REMAINDER'e böler ve kalan değer ne ise o REMAINDER'in bulunduğu partition tablosuna veriyi aktarır.

Örneğin MODULUS 10 dediğinizde partitioned tablosu 10 adet partition tablosuna sahip demektir ve tüm partition tablolarında sabittir. REMAINDER 0 dan 9 a kadar tüm değer alır. Böylece tablomuzu hangi sütuna göre böleceksek, o sütunun hash değerini MODULUS değerine böler ve örneğin kalan 3 ise REMAINDER 3 olan partition tablosuna veriyi aktarır.



Veri aktaralım.



İçeriğine baktığınızda p_status alanını random olarak atadığını görebilirsiniz. Random atadığı bu alana ait partitioning yaptık.



Her bir partition tablosuna aktarılan verileri görmek için partition tablolarına SELECT atıyorum. Partition tablolarına aktardığı verilerin sayısı birbirine yakın. Bu  sayıların yakın olmasının sebebi asında random() fonksiyonu kullandığım için birbirine yakın verilerin satır olarak eklenmesi. Tüm "waiting" kelimelerinin tek bir hash değeri vardır. Burada dikkat edilmesi gereken konu, eğer status gibi sabit değer alacak bir sütuna sahipseniz HASH partitioning yaparken verileri HASH değere bölmesi bu durumda anlamsız olacaktır. Tüm veriler "waiting" olsaydı eğer tüm waiting kelimesinin hash değeri aynı olacağı için hepsi tek bir partition tablosuna aktarılacaktı. Status alanına göre tablonuzu bölmek isterseniz LIST partitioning kullanmalısınız.



HASH partitioning yapmak için id, date gibi sürekli değişken değerler tutan sütunlarınızı kullanabilirsiniz. Böylece her bir hash value değişkeni için daha eşit sayılarda dağıtmış olur.


Dikkat edilmesi gereken bir konu da eğer eksik REMAINDER için HASH partition oluşturursanız, INSERT sırasında eksik REMAINDER için veri aktarılmak istendiğinde hata alacaksınız.

INSERT ON CONFLICT DO NOTHING

Bu ifadeyi insert sırasında herhangi bir çalışma olduğunda insert’ü engellemek için kullanıyoruz. PostgreSQL10 de bu ifadeyi kullanmak istediğimizde buna izin vermiyordu.



PostgreSQL11 ile bu özellik de geldi.



Sevgiler,

Comments

Popular posts from this blog

PostgreSQL High Availability - Patroni 2

PostgreSQL Foreign Data Wrappers

PostgreSQL High Availability - Patroni 1