Write Ahead Logging (WAL), veri bütünlüğünü(data integrity) sağlamaya yönelik standart bir yöntemdir. Postgresql, üzerlerinde yapılan, insert/update/delete işlemlerini, bilgilerin asıl tutulduğu diskteki Data Page’lere yazmanın yanında Transaction Log adı verilen log dosyalarına da yazarlar. Hatta verilerin  tutulduğu data page’den önce transaction log’u diske yazar, sonrasında ihtiyaca göre data page’e yazar. Hatta commit edilen uygulama başarılı bir şekilde Commit edildi bilgisini Transcation Log dosyası diske yazıldığında verirler, data Page’lere diske yazılmasını beklemezler. Transaction Log’lar ardışıl disk Page’lerinde oldukları için onların diske yazılması muhtemelen dağınık durumda bulunan Data Page’lerin diske yazılmasından daha hızlı olacaktır.

NOT : PostgreSQL performans için Atomicity ve Durability’den taviz  vermek isteyenlere özel Transcation Log’ların diske yazılma işleminin Asenkron yapılmasına da olanak tanır. Bu durumda Postgresql, Transaction Log’un diske yazılmasını beklemeden Transaction’ın başarılı olduğunu uygulamaya bildirir ancak tam bu sırada sunucu çökmesi durumunda veri kaybı yaşanabilir.

Transaction log, veritabanının önemli bir parçasıdır, çünkü veritabanı yönetim sisteminin bir sistem arızası meydana geldiğinde bile herhangi bir veriyi kaybetmemesi gerekir. Elektrik kesintisi veya sunucu çökmesine neden olan başka bir sunucu arızası gibi arızalar nedeniyle hiçbir verinin kaybolmamasını sağlamak için bir veritabanı sistemindeki tüm değişikliklerin ve eylemlerin bir geçmiş logudur. Log, halihazırda yürütülen her transaction hakkında yeterli bilgi içerdiğinden, veritabanı sunucusunun, sunucu çökmesi durumunda transaction log’daki değişiklikleri ve eylemleri yeniden çalıştırarak veritabanı clusterı kurtarabilmesi gerekir. Veri tabanında transaction log kayıtlarının tutulmadığı durumu düşündüğümüzde, shared buffer poolda değiştirilmiş ama diske henüz değişikliklerin kalıcı olarak yazılmadığı durumlarda bir kesinti veya veri tabanı kapanması olduğunda transactionların tabloya eklediği veriler kaybedilir.

NOT : Bu arada, crash durumunda recovery işleminin nasıl yapıldığına delecek olursak; Her bir checkpoint işleminden sonra transaction log dosyası sıfırlanır ve checkpoint ile ilgili bilgiler pg_control dosyasına yazılır, recovery işlemi yapılacağında sunucu öncelikle pg_control dosyasını ve checkpoint kaydını okur ve checkpoint kaydından sonraki transactionları REDO işleminden geçirir yani wal dosyalarından okur. pg_controldata ile controlfile’ın içine baktığımızda yukarıdada yazdığım gibi en sonki checkpointin LSN yazacaktır.

pg_controldata

Bu arada WAL transaction log’un eşanlamlısı olarak kullanılır ve aynı zamanda transaction log dosyasına  (WAL) yazma ile ilgili uygulanan bir mekanizmaya atıfta bulunmak için kullanılır.

WAL mekanizması, sunucu çökmelerinin etkilerini azaltmak için ilk olarak sürüm 7.1’de uygulanmıştır. Aynı zamanda, Point-in-Time Recovery (PITR) ve Streaming Replication (SR)uygulamalarını da mümkün kıldı.

WAL dosyalarında tutulan kayıtlara WAL data veya XLOG records denir. XLOG kayıtları memory’de WAL buffer alanında tutulur ve transaction commit veya rollback edildiğinde fiziksel WAL dosyalarına (WAL segment file) yazılır. LSN (Log Sequence Number) ise XLOG kayıtlarının transaction log dosyasında nerde bulunduğunu belirten unique numaradır.

PostgreSQL Write Ahead Logging (WAL)

  1. İlk adımda postgresql WAL kayıtlarını okur ve Şekil2’de gerçekleştirilen ilk insert işleminin XLOG kayıtlarından TABLE_A’nın ilgili page’ini shared buffer’a yükler.
  2. Bu aşamada tablonun LSN bilgisi ile XLOG kaydında yer alan LSN bilgisi karşılaştırılır. Eğer XLOG kaydının LSN bilgisi TABLO_A’nın LSN kaydından büyük ise bu transaction’lar yeniden işletilir. Eğer XLOG kaydın LSN bilgisi TABLE_A’nın LSN kaydından küçük ise bir işlem yapılmaz ve diğer WAL datanın okunmasına geçilir.
  3. adımda yapılan işlemler tekrar edilir ve kayıp diğer transaction’ın gerçekleştirdiği veri’de TABLO_A’ya insert edilir.

Transaction logları PostgreSQL’de veri dizininin altındaki pg_wal dizininde tutulur. Her bir transaction log dosyası 16 MB’lık yer tutar(PostgreSQL cluster initdb komutu ile oluşturulduğunda, WAL segment dosyasının boyutu –wal-segsize seçeneği kullanılarak yapılandırılabilir).  wal dosya adları 24 karakterden oluşur ve değerler hexadecimal olarak tutulur. İlk 8 karakter timeline bilgisini verir. Sonraki 16 karakter ise WAL idsini verir, 0000000000000001 ile başlar ve artarak devam eder. Timeline değeri ise her Point-in-Time Recovery (PITR)’den sonra bir artar.Örnek;

000000010000000000000001
000000010000000000000002
0000000100000000000000FF
000000010000000100000000  

Aşağıdaki komutla belirtilen LSN’yi içeren wal dosyasını bulabiliriz;

SELECT pg_walfile_name('1/00002D3E');

Halihazırdaki LSN’yi aşağıdaki komut ile öğrenebiliriz.

postgres=# SELECT pg_current_wal_lsn();
-[ RECORD 1 ]------+-----------
pg_current_wal_lsn | 0/925D7E70

İstersek current lsn fonksiyonu da kullanarak şu anda aktif olan wal dosyasınıda aşağıdaki gibi öğrenebiliriz.

postgres=# select pg_walfile_name(pg_current_wal_lsn());
     pg_walfile_name      
--------------------------
 000000010000000000000002

Eğer wal dosyasını değiştirmek istersek de aşağıdaki fonsiyonu çağırabiliriz;

select pg_switch_wal();

Her wal dosyasının 16 MB olduğunu söyledik internal olarak bu dosya 8 KB’lık page lerden meydana gelir. İlk page’de XLogLongPageHeaderData yapısıyla tanımlanan bir header verisi bulunurken, diğer tüm pagelerin başlıkları XLogPageHeaderData yapısıyla tanımlanan page bilgilerine sahiptir. Page headerının ardından, XLOG kayıtları her page’e baştan itibaren azalan sırayla yazılır.

PostgreSQL Write Ahead Logging (WAL)

Örnek wal verilerine bakmak istersek /usr/pgsql-12/bin/pg_waldump aracını kullanabiliriz.

-bash-4.2$ /usr/pgsql-12/bin/pg_waldump  /database/data/pg_wal/000000010000000000000001  |more
rmgr: XLOG        len (rec/tot):    114/   114, tx:          0, lsn: 0/01000028, prev 0/00000000, desc: CHECKPOINT_SHUTDO
WN redo 0/1000028; tli 1; prev tli 1; fpw true; xid 0:3; oid 12000; multi 1; offset 0; oldest xid 3 in DB 1; oldest multi
 1 in DB 1; oldest/newest commit timestamp xid: 0/0; oldest running xid 0; shutdown
 7, blkref #0: rel 1663/1/2673 blk 1
rmgr: Btree       len (rec/tot):     72/    72, tx:          4, lsn: 0/010924D0, prev 0/01092488, desc: INSERT_LEAF off 6

WAL Parametreleri

Postresql.conf dosyasında WAL dosyalarının yönetimi ile ilgili parametreler bulunur.

wal_level  : Wal_level önemli bir parametredir, çünkü bu ayar WAL ile yapabileceklerimiz üzerinde doğrudan bir etkiye sahiptir, sadece çökmeden kurtarmak için kullanabileceğimiz gibi, bir noktadan sonra kurtarma için arşivlemek için de kullanabiliriz veya bir stand-by sunucusu oluşturmak içinde kullanabiliriz. Yani wal_level parametresi ile WAL’a ne kadar bilgi yazılacağını belirler. Varsayılan değer, yedek bir sunucuda salt okunur sorguların çalıştırılması dahil, WAL arşivlemeyi ve çoğaltmayı desteklemek için yeterli veriyi yazan replica’dır. minimal, bir çökme veya anında kapanma durumundan kurtarmak için gereken bilgiler dışındaki tüm log kaydını kaldırır. logical, logical decoding için gerekli bilgileri ekler.

fsync : Boolean değer alan (on/off) bu parametre ile PostgreSQL’in WAL işlemlerini diske wal_sync_method ile belirtilen sistem çağrısı ile yapıp yapmayacağını belirtebilirsiniz. fsync parametresi açık (on) olduğu zaman WAL dosyaları işletim sistemi tarafından anında diske yazılır. Bu parametre kapalı olduğunda, işletim sistemi transaction logları bufferda saklar ve belirli aralıklarla yazar. Ancak bu bir veri kaybı riski getirir: Olası bir elektrik kesintisinde, vs, henüz diske commit edilmemiş transactionlar varsa onlar kaybolur. Bu parametreyi açık tutmak veri bütünlüğü için önemlidir

synchronous_commit: on, off, local ve remote_write değerlerini bu parametre ile WAL kayıtlarının diske bir miktar gecikmeli yazılmasını sağlayabilirsiniz. Öntanımlı değeri on’dur. Bu parametreyi kapattığınızda, okuma işlemlerinin çok ağırlıklı olduğu veritabanlarında bir miktar kaybı göze alıp önemli bir başarım artışı sağlayabilirsiniz. Off olunca, istemciye onay döndürülmesinden sonra transaction xlog’a gerçekten yazılır. Gecikme süresi en fazla wal_writer_delay değerinin 3 katıdır. Bu gecikme sıralı olacağı için, çökme durumunda veritabanı bütünlüğü bozulmaz. Sadece veri kaybı olur. PostgreSQL 9.1 ile birlikte gelen “synchronous replication” özelliği ile birlikte bu parametrenin en bir özelliği daha olmuştur. Senkron replikasyonu etkinleştirmek için kullanılan synchronous_standby_names parametresi, synchronous_commit parametresinin de on olması ile anlamlıdır. Eğer bu değer remote_write olursa, o zaman transaction bilgisinin standby sunucuda OS buffer’lara yazılması yeterlidir – transaction commit edilmiş sayılır. Bu durumda, PostgreSQL standby sunucuda çökse de veri kaybı olmaz, ama işletim sistemi çökerse veri kaybı kaçınılmaz olur.

wal_sync_method   : WAL güncellemelerini diske göndermeye zorlamak için kullanılan yöntem. Fsync kapalıysa, WAL dosya güncellemeleri hiç zorlanmayacağından bu ayar geçersizdir. Alabileceği parametreler aşağıdaki gibidir. fsync ayarı, her transaction’ın her commit’den sonra sabit diske yazılmasını zorlar. Bunu kapatmak, özellikle toplu yükleme işlemleri için performansı artıracaktır. Fsync’in devre dışı bırakılması, elektrik kesintisi ve çökmeler durumunda veri tutarlılığı gibi ciddi sorunlara yol açabilir.

  • open_datasync
  • fdatasync
  • fsync
  • fsync_writethrough
  • open_sync

full_page_writes : Öntanımlı olarak on’dur. Bu parametre açık olduğunda, PostgreSQL sunucusu, checkpoint işleminden sonra o page’in ilk değişikliği sırasında her disk page’nin tüm içeriğini WAL’a yazar.  Veri güvenliği açısında bu parametre açık olmalıdır.

wal_log_hints  : checksum aktif değilse; bu parametre açık olduğunda, PostgreSQL sunucusu kritik olmayan değişiklikler için bile checkpoint’den sonra her page’i wal’a yazar.

wal_compression : Bu parametre açıkken, PostgreSQL sunucusu, full_page_writes açıkken veya bir bae backup sırasında WAL’a yazılan bir tam page image’ını sıkıştırır. Varsayılan değer kapalı. Bu parametrenin açılması, kurtarılamayan veri bozulması riskini artırmadan WAL hacmini azaltabilir, ancak WAL logu sırasında sıkıştırma ve WAL yeniden oynatma sırasında sıkıştırmanın açılması sırasında harcanan fazladan CPU maliyetiyle.

wal_buffers: WAL verisi için shared_buffers içinde kullanılan bellek miktarıdır. Bu değer, bir transaction tarafından üretilen WAL verisi kadar olmalıdır. 9.1 sürümü itibariyle öntanımlı değeri -1’dir, ve shared_buffers değerinin %3’üne karşılık gelir. Buna “otomatik wal buffer yönetimi” denir.

wal_writer_delay: milisaniye olarak belirtilebilen bir değerdir. 1 ile 10000 ms arasında olabilir. WAL writer sürecinin “uyuma” sürecini ayarlar. Belirtilen süre boyunca WAL writer süreci uyur, ve ertesinde WAL bilgisini diske yazar. Öntanımlı değer 200 milisaniyedir.

commit_delay: Mikrosaniye cinsinden belirtilen bu değer, bir commit kaydının WAL buffer’a yazılması ve bunun diske yazılması arasındaki bekletme süresini gösterir. Bu sürenin artması ile birçok transaction aynı anda diske yazılabilirler (fsync) . Yoğun sistemler/saatler için bu değerin artması iyi olabilir. Ancak az transaction olan saatlerde / veritabanı sunucularında bu değerin yüksek olması transactionların geç commit edilmesine neden olabilir. Bu nedenle, bu süre eğer aşağıda yazdığım commit_siblings kadar açık transaction, sunucunun commit kaydını yazdığı zaman anında etkin ise geçerli olur. Öntanımlı değeri 0 ms’dir.

commit_siblings : commit_delay parametresinde belirtilen gecikme süresinin uygulanabilmesi için gereken en az sayıda eşzamanlı açık transaction sayısını gösterir. Yüksek değerlerde en az bir tane daha transactionın commit_delay süresinde commit için hazır olacağını söyleyebiliriz.

WAL arşivleme kullanılmadığında, sistem normalde sadece birkaç segment dosyası oluşturur ve daha sonra artık ihtiyaç duyulmayan segment dosyalarını daha yüksek segment numaralarına yeniden adlandırarak bunları “geri dönüştürür”. İçeriği son ckeckpoint’den önce gelen segment dosyalarının artık ilgi konusu olmadığı ve geri dönüştürülebileceği varsayılmaktadır.

WAL verilerini arşivlerken, doldurulduktan sonra her bir segment dosyasının içeriğini kaydetmemiz ve segment dosyası yeniden kullanım için geri dönüştürülmeden önce bu verileri bir yere kaydetmemiz gerekir. WAL arşivlemeyi etkinleştirmek için wal_level yapılandırma parametresini replika veya daha yüksek bir değere ayarlayın, archive_mode’u açık yapın ve archive_command yapılandırma parametresinde kullanılacak shell komutunu belirtin.

# Copy the file to a safe location (like a mounted NFS volume)
archive_command = 'cp %p /mnt/nfs/%f'

# Not overwriting files is a good practice
archive_command = 'test ! -f /mnt/nfs/%f && cp %p /mnt/nfs/%f'

# Copy to S3 bucket
archive_command = 's3cmd put %p s3://BUCKET/path/%f'

# Copy to Google Cloud bucket
archive_command = 'gsutil cp %p gs://BUCKET/path/%f'

# An external script
archive_command = '/opt/scripts/archive_wal %p'

archive_timeout : Archive_command yalnızca tamamlanmış WAL segmentleri için çağrılır. Dolayısıyla, sunucunuz çok az WAL trafiği oluşturuyorsa, bir işlemin tamamlanması ile arşiv depolamasına güvenli kaydı arasında uzun bir gecikme olabilir. Arşivlenmemiş verilerin kaç eski olabileceğini sınırlamak için, archive_timeout ayarını sunucuyu periyodik olarak yeni bir WAL segment dosyasına geçmeye zorlayacak şekilde ayarlayabilirsiniz.

Mustafa Bektaş Tepe
İyi Çalışmalar

Referanslar;
http://www.interdb.jp/pg/pgsql09.html

https://medium.com/@gokhansengun/write-ahead-logging-nedir-ve-rdbmsler-neden-kullan%C4%B1r-56d7956ec93
https://habr.com/en/company/postgrespro/blog/491730/
http://www.veritabaniegitimleri.com/2020/04/30/postgresql-wal-write-ahead-log-dosyalari/
https://www.postgresql.org/docs/current/wal-intro.html
https://blog.gunduz.org/index.php?/archives/140-PostgreSQLde-WAL-Kavram.html

Loading