İçindekiler


Detaylara girmeden önce kavramları netleştirmemiz gerektiğini düşünüyorum çünkü kavramlar farklı veritabanları için değişebiliyor hatta aynı veritabanı için aynı kavram veritabanı yöneticisi ve yazılımcı için bile farklı anlamlara gelebiliyor. Bu nedenle Postgresql’deki kavramları kısaca anlatmak istiyorum.

PostgreSQL’de her tablo, sütun koleksiyonunun isimlendirlmiş halidir. Tablolar, veritabanları halinde gruplandırılır yani veritabanları tablo koleksiyonlarıdır ve tek bir PostgreSQL instance(verilere erişimi idare eden) tarafından yönetilen veritabanı koleksiyonuna, veritabanı cluster denir. Bu kısmı önemsediğim için elimden geldiğince kısa ve öz yazmaya çalıştım ve bu parağrafdan anlaşılmasını istedikleirm PostgreSQL’de tablo, veritabanı, veritabanı cluster ve instance kavramları.

Bir sunucuda birden fazla cluster çalışabilir ama her cluster kendi data dizininde çalışması gerekir yani başka bir deyişle bir data dizininde sadece bir tane cluster çalışabilir. Bu arada bir sunucuda birden fazla cluster çalışıyorsa her cluster için farklı port kullanılması gerekir eğer aynı port kullanılsın isteniyorsa o zaman da sunucuda birden fazla ip olması lazım.

NOT : Yukarıdaki özellikle instance ve cluster kavramlarını Oracle veritabanlarındaki anlamlarıyla karıştırmamak gerekir.

Cluster’ın Mantıksal Yapısı

Cluster, PostgreSQL sunucusu tarafından yönetilen veritabanı koleksiyonudur. PostgreSQL’deki “veritabanı cluster” terimi, “bir grup veritabanı sunucusu” anlamına gelmez. Bir PostgreSQL sunucusu, tek bir ana bilgisayarda çalışır ve tek bir veritabanı clusterını yönetir. Veritabanı, veritabanı nesnelerinin bir koleksiyonudur. İlişkisel veritabanı teorisinde, veritabanı nesnesi verileri depolamak veya referans vermek için kullanılan veri yapısıdır. PostgreSQL’de veritabanları aynı zamanda veritabanı nesneleridir ve mantıksal olarak birbirlerinden ayrılırlar. Diğer tüm veritabanı nesneleri (örn. Tablolar, dizinler, vb.) İlgili veritabanlarına aittir. Aslında aşağıdaki resim herşeyi özetliyor.

PostgreSQL Cluster

PostgreSQL’deki tüm veritabanı nesneleri, 4 baytlık integer olan unique bir OID (object id) değeri bulunur. Veritabanı nesneleri ve ilgili OID’ler arasındaki ilişkiler, nesnelerin türüne bağlı olarak uygun sistem kataloglarında saklanır. Örneğin, veritabanlarının OID’leri ve heap tabloları sırasıyla pg_database ve pg_class’da saklanır, böylece aşağıdaki gibi sorgular çalıştırarak bilmek istediğiniz OID’leri bulabilirsiniz:

 SELECT datname, oid FROM pg_database WHERE datname = 'sampledb';
 datname  |  oid  
----------+-------
 sampledb | 16384
(1 row)

sampledb=# SELECT relname, oid FROM pg_class WHERE relname = 'sampletbl';
  relname  |  oid  
-----------+-------
 sampletbl | 18740 
(1 row)kullanı

Cluster’ın Fiziksel Yapısı

Cluster’ın fiziksel yapıları ise disk üzerinde somut olarak bulunan dosyalardır. PGDATA dizinin altında ve alt dizinlerinde bulunan dosyalar veri tabanı fiziksel bileşenlerini oluşturur. Zorunlu olmamasına rağmen, base directory olarak genellikle PGDATA ortam değişkenine ayarlanır.

Aşağıdaki resim PostgreSQL’deki veritabanı cluster’ın bir örneğini göstermektedir. Veritabanı, PGDATA dizini altındaki, base dizininin altındaki bir alt dizindir ve tabloların ve indexlerin her biri, ait olduğu veritabanının alt dizini altında depolanan (en az) bir dosyadır. Ayrıca belirli verileri ve yapılandırma dosyalarını içeren birkaç alt dizin vardır. PostgreSQL’deki tablespace, base dizinin dışındaki bazı verileri içeren bir dizindir.

Postgresql Data File

PGDATA dizini altında veri tabanı dosyaları alt dizinler şeklinde OID değerlerine göre ayrılır. PGDATA altında bulunan dosyalar şunlardır;

Dosyalar Açıklama
PG_VERSION PostgreSQL’in ana sürüm numarasını içeren bir dosya
pg_hba.conf host-based authentication anlamına gelen dosya, istemcilerin kimlik doğrulamasını kontrol etmek kullanılan dosyadır.
pg_ident.conf Postgresql user name mapping işlem bilgilerini tutan dosyadır.
postgresql.conf Konfigürasyon parametrelerinin tutulduğu dosyadır.
postgresql.auto.conf ALTER SYSTEM komutu ile set edilen konfigürasyon parametrelerinin tutulduğu dosyadır.
postmaster.opts Sunucunun en son nasıl çalıştırıldığını gösteren komut satırını tutar.
postmaster.pid Proses PID , postgresql base dizini, shared memory shmid değeri gibi bilgileri tutar.
current_logfiles current_logfiles, geçerli log yazma dosyasını depolayan bir meta veri dosyasıdır. Bu dosya initdb zamanında ayarlanan grup erişim modunu izlemez, ancak log_file_mode izinlerini izler.

 

Altdizinler Açıklama
base base/ dizinin altında her veri tabanı için bir dizin oluşturulur ve ilgili veri dosyaları bu dizinlerde tutulur.
global global/ dizini altında cluster bazında bilgileri tutan tablolar’ın dosyaları yer alır. (Örneğin pg_database, pg_control)
pg_commit_ts Transaction commit timestamp verilerini içeren alt dizin.
pg_dynshmem Dynamic shared memory tarafından kullanılan dosyaları içeren alt dizin.
pg_logical Logical decoding için durum verilerini içeren alt dizin.
pg_multixact multitransaction durumlarını tutan dizin. shared-row-lock mekanizmasının ana bölümüdür.
pg_notify LISTEN/NOTIFY durum verilerini içeren alt dizin.
pg_repslot Replication slot verilerini içeren alt dizin.
pg_serial Commit edilmiş serializable transaction lar hakkında bilgi veren alt dizin.
pg_snapshots Export alınan snapshotları içeren alt dizin.
pg_stat İstatistik için kalıcı dosyalar içeren alt dizin.
pg_stat_tmp İstatistik için geçici dosyalar içeren alt dizin.
pg_subtrans Subtransaction durum verilerini içeren alt dizin.
pg_tblspc Tablespace lere symbolic link içeren alt dizin.
pg_twophase Prepared transaction’lar için durum bilgilerini tutan alt dizin. Her bir global transaction için adı verilen bir global transaction identifier tanımlanır. Client PREPARE TRANSACTION ile postgres transaction’ına GID değerini atar. Tüm aktif transaction’lar shared-memory dizisinde tutulduğundan PREPARE TRANSACTION komutu verildiğinde GID değeri dizideki işlem için ayrılır. Sistemin çökmesi veya kapanması durumunda prepared durumundaki işlemlerin saklanması gerekir.
pg_wal WAL (Write Ahead Logging) segment dosyalarını içeren alt dizin. Sürüm 10’dan önce pg_xlog adında kullanılırdı.
pg_xact Transaction ID (XID)  değeri atanmış her bir transaction’ın commit durum bilgisini içeren alt dizin. Sürüm 10’dan önce pg_clog adında kullanılırdı.

Postgresql Mimarisi

Yukarıda kısaca bahsettiğim gibi $PGDATA/base dizi altında her veritabanı için veritabanının OID numarasıyla isimlenmiş bir dizin daha vardır. Örneğin testdb veritabanı için base altında 16384 isminde biz dizin daha oluşturulmuş.

Postgresql architecture

Tablolar ve indexler için durum nasıl diye soracak olursanız?  Boyutu 1 GB’den küçük olan her tablo veya index, ait olduğu veritabanı dizini altında depolanan tek bir dosyadır. Veritabanı nesneleri olarak tablolar ve indexler internal olarak özgün OID’ler tarafından yönetilirken, bu veri dosyaları relfilenode değişkeni tarafından yönetilir. Bu arada OID değeri ve relfilenode değeri her zaman aynı olmayabilir. Mesela bir tablo da truncate komutu çalıştırılmışsa relfilenode değeri değişmiştir.

Postgresql infrastructure

NOT : pg_relation_filepath fonksiyonu ile belirtilen OID veya adla ilişkinin dosya yolu adını döndürdüğü için kullanışlıdır.

 testdb=# SELECT pg_relation_filepath('testtable');
 pg_relation_filepath 
----------------------
 base/16384/16392
(1 row)

Tabloların ve dizinlerin dosya boyutu 1 GB’ı aştığında, PostgreSQL relfilenode.1 gibi yeni bir dosya oluşturur ve kullanır. Yeni dosya doldurulmuşsa, relfilenode.2 gibi bir sonraki yeni dosya oluşturulur ve bu böyle devam eder.

$ ls -la -h base/16384/19427*
-rw------- 1 postgres postgres 1.0G  Apr  21 11:16 data/base/16384/19427
-rw------- 1 postgres postgres  45M  Apr  21 11:20 data/base/16384/19427.1 

NOT : Tabloların ve indexlerin maksimum dosya boyutu, PostgreSQL oluştururken yapılandırma, –with-segsize seçeneği kullanılarak değiştirilebilir.

Veritabanı alt dizinlerine baktığımızda, her tablonun sırasıyla ‘_fsm’ ve ‘_vm’ ile son eklenmiş iki ilişkili dosya olduğunu göreceksiniz. Bunlar free space map ve visibility map olarak adlandırılır. Indexler  yalnızca free space map sahiptir visibility map dosyalarına sahip değildir.

  • free space map : Her FSM, her page’in boş alan kapasitesi hakkındaki bilgileri ilgili tablo veya dizin dosyasında saklar. Tüm FSM’ler ‘fsm’ sonekiyle saklanır ve gerekirse paylaşılan belleğe yüklenir. Tabloya veya indexe veri eklenirken , PostgreSQL eklenebilecek pageleri seçmek için karşılık gelen tablonun veya indexin FSM’sini kullanır.
  • visibility map : Her tablonun, tablo dosyasındaki her page’in görünürlüğünü tutan ayrı bir görünürlük haritası vardır. Pagelerin visibility’si, her sayfada dead tuple olup olmadığını belirler. Vacuum’da bu şekilde dead tüple olmayan pageleri atlayabilir.

Tablespaces

PostgreSQL’deki tablespace, base dizinin dışındaki ek bir veri alanıdır. Kullanım alanını bir örnekle açıklarsak daha anlaşılır olacaktır; Kurulum yaparken farklı bir dizin belirtmediyseniz ve default dizin olduysa ve hiçbir şekilde dizini büyütülemiyorsa, bu sizin veritabanızın çalışmasını engeller fakat farklı bir disk de tablespace oluşturulup sistem yeniden yapılandırılırsa sorun olmayacaktır. Yada veritabanlarınızın bazıları hızlı çalışması gerekebilir ve bunları ssd dizinine yerleştirerek performansı artırabilirsiniz ya da arşivleme yapılan uygulama da eski dataları yavaş disk üzerinde tablespace oluşturarak  maliyeti düşürebilirsiniz. Tablespace’in clusterdaki yerini anlamak adına aşağıdaki resim yararlı olacaktır.

Postgresql-ROLE-USER-SCHEMA

Veritabanları, şemalar, tablolar, indexler ve sequenceler tablespaceler içinde oluşturulabilir. Bunu yapmak için o tablespace üzerinde CREATE yetkisi olan kullanıcı ilgili komuta tablespace adını bir parametre olarak vermemiz gerekmektedir.

İki adet default tablespace vardır.

  • Pg_defaulttablespace bütün userların verisini tutan tablespace’dir.
  • Pg_globaltablespace ise global veriyi depolar.

Postgresql Tuple

Tablespace dizini, pg_tblspc alt dizininden symbolic link ile adreslenir ve bağlantı adı, tablespace OID değeri ile aynıdır.

mkdir /testtbs
chown postgres:postgres  /testtbs
CREATE TABLESPACE testtbs LOCATION '/testtbs';
create database testdb1 TABLESPACE testtbs; 

Postgresql Data directory

CREATE TABLESPACE sorgusunu çalıştırdığımızda belirtilen dizin altında tablespace oluşturulur ve bu dizin altında sürüme özgü alt dizin oluşturulur. Tablespace altında yeni bir veritabanı oluşturursanız, dizini sürüme özgü alt dizin altında oluşturulur.

Postgresql create database

\db veya \db+ komutu ve pg_tablespace sistem katoloğunu kullanarak tablespaceler görüntülenebilir.

Aşağıdaki komut ile tablo başka bir tablespace’e taşınabilir.

alter table tablename set tablespace tablespace_name; 

Aşağıdaki komut ile tablespacedeki bütün tablolar başka bir tablespace’e taşınabilir.

 alter table all in tablespace tablespace_name set tablespace tablespace_name;

Aşağıdaki komut tabloların hangi tablespace üzerinde tutulduğunu öğrenmek için kullanılır.

select * from pg_tables where tablespace = 'tablespacename'; 

Aşağıdaki gibi default tablespace’i belirleyebiliriz.

SET default_tablespace = tablespacename; 

Datafile Yapısı

Datafile kendi içinde  sabit uzunlukta pagelere (veya bloklara) bölünmüştür, varsayılan değer 8192 bayttır (8 KB). Her dosyadaki bu sayfalar 0’dan başlayarak sırayla numaralandırılır ve bu tür numaralar blok numaraları olarak adlandırılır. Dosya doldurulmuşsa, PostgreSQL dosya boyutunu artırmak için dosyanın sonuna yeni bir boş page ekler. Page lerin iç düzeni, data file türlerine bağlıdır.

Postgresql create database

  • heap tuple : heap tuple verilerinin kendisidir. Pagelerin altından itibaren sırayla dizilirler.
  • line pointer : 4byte long bir id değeridir. Ve her heap tuple için bir tane pointer bulunur. Heap tuple’lar için index görevi görürler. Page’e yeni bir tuple eklendiğinde yeni bir pointer da eklenir.Aynı zamanda item pointer olarak da adlandırılır.
  • header data : her page’in başında yer alan 24 byte’lık alandır ve page hakkında tanımlayıcı bilgileri tutar.
  • Pd_lsn: Bu alanda WAL kayıtlarında bu pagede en son değişiklik yapan kaydın bilgisi tutulur. 8byte’tır.
  • Pd_checksum : Bu alanda page’in checksum değeri tutulur.
  • Pd_lower/Pd_upper : pd_lower line pointer’ın bittiği yeri işaret eder.Pd_newest ise en yeni heap tupple’ın yerini işaret eder.
  • Pd_special : Bu değişken indeksler içindir. Tablolar içindeki page’de, page’in sonunu gösterir. İndeksler içindeki page’de sadece indeksler tarafından tutulan veri alanı olan ve B-tree, GiST, GiN gibi indeks türlerinin türüne göre belirli verileri içeren özel boşluğun başlangıcına işaret eder.

Line pointerdan sonra, en yeni tuple’ın başına kadar olan boş alan free space veya hole olarak isimlendirilir.

Bir tabloda tuple’ı belirlemek için TID – tuple identifier değeri kullanılır. TID, bir çift değer içerir: tuple’ı içeren page’in blok numarası ve tuple’a işaret line pointer’ın ofset numarası.

Tuple Yazma ve Okuma işlemleri

Yalnızca heap tuple içeren page’den oluşan bir tablo varsayalım. Bu page’in pd_lower’ı ilk line pointerı ve hem line pointer hem de pd_upper’ı ilk heap tuple’ı işaret eder. İkinci tuple geldiğinde, birincisinin arkasına yerleştirilir. İkinci line pointer birinciye itilir ve ikinci tuple’ı işaret eder. Pd_lower, ikinci line pointer ve pd_upper’ı ikinci heap tüple gösterecek şekilde değişir. Page’deki diğer header verileri (örneğin, pd_lsn, pg_checksum, pg_flag) da uygun değerlere yeniden yazılır;

Postgresql datafile tuple

Okuma işlemi sırasında ise Sequential scan ve B-tree Index scan olmak üzere en çok kullanılan 2 farklı yol vardır.

Sequential scan : bütün page lerde yer alan bütün tuple’lar line pointer’lar aracılığı ile tek tek sıralı bir şekilde okunur.

B-tree index scan : Index dosyaları TID ve index key değerinden oluşan Index Tuple’larından oluşur. Index dosyalarında eğer aranan index key değeri bulunur ise ona bağlı TID değeri okunarak direkt olarak ilgili Page ve Tuple’a gidilerek sonuç getirilir.

Postgresql tuple architecture

NOT : PostgreSQL ayrıca TID-Scan, Bitmap-Scan, Index-Only-Scan aramayıda destekler.

Mustafa Bektaş Tepe
İyi Çalışmalar

Loading