İç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’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.
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ı. |
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ş.
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.
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.
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.
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;
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.
\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.
- 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;
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.
NOT : PostgreSQL ayrıca TID-Scan, Bitmap-Scan, Index-Only-Scan aramayıda destekler.
Mustafa Bektaş Tepe
İyi Çalışmalar