pg_stat_bgwriter ve pg_stat_checkpointer view’larının özelliklerini ve process’lerin davranışlarını inceleyeceğimiz bu yazımıza öncelikle konunun geçmişinden bahsederek başlayalım. Daha öncesinde “Background Writer” (bgwriter) process’inin görevi olan “background write” ve “checkpoint” işlemleri PostgreSQL 9.2 versiyonunda iki ayrı process’e bölündü ve “background write” işlemini bgwriter, checkpoint işlemini de checkpointer process’i yapacak şekilde güncellendi. (9.2 versiyonundan önce checkpointer process’i yoktu). Bu değişikliğin en önemli nedenlerinden biri şu linkte de görebileceğiniz gibi checkpoint’in son fsync işlemini yapabilmek için “background write” işlemini durdurma zorunluluğuydu ve bunun gibi faktörler negatif performans etkisi yaratıyor idi. 9.2 versiyonunda bahsettiğimiz değişiklik yapıldı ve checkpointer process’i devreye alındı, ancak background process’lerde yapılan bu değişiklik, istatistik view’larına yansımadı ve her iki process’e ait bilgiler pg_stat_bgwriter view’ında tutulmaya devam etti. PostgreSQL 17 versiyonunda ise bu istatistikler de artık farklı view’larda tutulmaya başlandı.
Bu arada, “background write” ve “checkpoint” işlemlerini hatırlayacak olursak, öncelikle her iki işlemin de shared buffers’da bulunan “dirty” yani commit’lenmiş ancak disk üzerindeki data file’da güncellemesi yapılmamış veri bloklarını diske yazdığını belirtelim. Checkpoint işlemi her “checkpoint_timeout” saniyede bir, ya da WAL segment’lerin boyutunun “max_wal_size” ı aşması ile tetiklenir. Diğer yandan “background write” işlemi, shared buffers üzerindeki temiz/kullanılabilir blokların sayısı yetersiz görüldüğünde tetiklenir. Bu tetikleme sonucunda buffer alanındaki dirty blokların bir kısmı diske yazılarak temiz işaretlenir ve shared buffer’da yer açılır.
PostgreSQL dökümantasyonundan da checkpoint ve Background Writer kavramları ile ilgili detayları okuyabilirsiniz.
https://www.postgresql.org/docs/current/wal-configuration.html
https://www.postgresql.org/docs/current/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-BACKGROUND-WRITER
PostgreSQL 17 versiyonu ile birlikte view’ların kolonları şu şekilde yapılandırıldı:
postgres=# \d pg_stat_bgwriter
View "pg_catalog.pg_stat_bgwriter"
Column | Type | Collation | Nullable | Default
------------------+--------------------------+-----------+----------+---------
buffers_clean | bigint | | |
maxwritten_clean | bigint | | |
buffers_alloc | bigint | | |
stats_reset | timestamp with time zone | | |
postgres=# \d pg_stat_checkpointer
View "pg_catalog.pg_stat_checkpointer"
Column | Type | Collation | Nullable | Default
---------------------+--------------------------+-----------+----------+---------
num_timed | bigint | | |
num_requested | bigint | | |
restartpoints_timed | bigint | | |
restartpoints_req | bigint | | |
restartpoints_done | bigint | | |
write_time | double precision | | |
sync_time | double precision | | |
buffers_written | bigint | | |
stats_reset | timestamp with time zone | | |
Şimdi testlerimizin ilk aşamasında, yeni bir PostgreSQL cluster yaratıyor ve veri tabanımızda bu iki view’ı sorguluyoruz:
postgres=# select * from pg_stat_bgwriter;
buffers_clean | maxwritten_clean | buffers_alloc | stats_reset
---------------+------------------+---------------+-------------------------------
0 | 0 | 175 | 2024-11-25 13:23:41.271027+00
(1 row)
postgres=# select * from pg_stat_checkpointer;
num_timed | num_requested | restartpoints_timed | restartpoints_req | restartpoints_done | write_time | sync_time | buffers_written | stats_reset
-----------+---------------+---------------------+-------------------+--------------------+------------+-----------+-----------------+-------------------------------
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2024-11-25 13:23:41.271027+00
(1 row)
pg_stat_bgwriter çıktısında sadece “buffers_alloc” değerinin sıfırdan farklı olduğunu görüyoruz. Bu değer cluster bazında her “buffer allocation” işleminde artan bir counter değeri ve background writer process’i bu değeri kendi algoritmasında, ne zaman çalışacağını/devereye gireceğini belirmede kullanıyor. Diğer kolonlardan “buffers_clean”, bgwriter tarafından temizlenilen buffer sayısını, “maxwritten_clean” ise bgwriter process’inin bir temizleme işleminde üst sınırından daha fazla buffer temizlemesi sonucunda durmasının, kaç defa yaşandığını gösteriyor. Yeni veri tabanımızda bu değerlerin 0 olması bekliyoruz. Aynı zamanda pg_stat_checkpointer çıktısındaki tüm kolonların da yine 0 olduğunu görüyoruz.
Şimdi bir tablo yaratıp, içine kayıt insert ederek view’ları tekrar inceleyelim:
postgres=# create table io_test (c1 integer,c2 text);
postgres=# INSERT INTO io_test( c1, c2)
SELECT floor(random() * 999 + 1)::int,
left(md5(random()::text),3)
FROM generate_series(1,3000000);
INSERT 0 3000000
postgres=# select * from pg_stat_bgwriter;
buffers_clean | maxwritten_clean | buffers_alloc | stats_reset
---------------+------------------+---------------+-------------------------------
0 | 0 | 13583 | 2024-11-25 13:23:41.271027+00
(1 row)
postgres=# select * from pg_stat_checkpointer;
num_timed | num_requested | restartpoints_timed | restartpoints_req | restartpoints_done | write_time | sync_time | buffers_written | stats_reset
-----------+---------------+---------------------+-------------------+--------------------+------------+-----------+-----------------+-------------------------------
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2024-11-25 13:23:41.271027+00
(1 row)
Yaptığımız işlem ile 3 milyon kaydı tabloya insert ettik ve view’ları sorguladık. Ancak henüz ne checkpoint ne de bgwriter tetiklenmediği için, tüm insert işleminin memory’de yapıldığı ve diske henüz hiçbir veri yazılmadığını söyleyebiliriz. Sadece insert işlemi ile birlikte shared buffers’da “buffer allocation” yapıldığını ve buffers_alloc değerinin arttığını görüyoruz.
Şimdi manuel bir checkpoint tetikleyelim ve pg_stat_checkpointer view’ını tekrar inceleyelim.
postgres=# checkpoint;
CHECKPOINT
postgres=# select * from pg_stat_checkpointer;
num_timed | num_requested | restartpoints_timed | restartpoints_req | restartpoints_done | write_time | sync_time | buffers_written | stats_reset
-----------+---------------+---------------------+-------------------+--------------------+------------+-----------+-----------------+-------------------------------
0 | 1 | 0 | 0 | 0 | 397 | 84 | 13315 | 2024-11-25 13:23:41.271027+00
(1 row)
postgres=# select * from pg_stat_bgwriter;
buffers_clean | maxwritten_clean | buffers_alloc | stats_reset
---------------+------------------+---------------+-------------------------------
0 | 0 | 13839 | 2024-11-25 13:23:41.271027+00
(1 row)
Checkpoint ile birlikte 13315 dirty buffer bloğunun diske yazıldığını (buffers_written), ve işlemin 481 ms. (write_time+sync_time) sürdüğünü görebiliyoruz. Aynı bilgi “log_checkpoints” parametresi açık olduğunda log dosyasına da yazılacaktır. Ayrıca view çıktısından sadece 1 defa “requested checkpoint”, yani checkpoint_timeout süresi dolmadan istek üzerine checkpoint alındığını (num_requested=1) ve henüz hiç checkpoint_timeout süresi dolması sonucu otomatik checkpoint alınmadığını (num_timed=0) görebiliyoruz.
Şimdi shared buffer’da boş alan baskısı yaratmak için tablodaki verinin tamamını okuyup yeniden tabloya yazalım ve bir kaç saniye sonrasında view’ları inceleyelim.
postgres=# insert into io_test select * from io_test ;
INSERT 0 3000000
postgres=# select * from pg_stat_bgwriter;
buffers_clean | maxwritten_clean | buffers_alloc | stats_reset
---------------+------------------+---------------+-------------------------------
4466 | 20 | 27558 | 2024-11-25 13:23:41.271027+00
(1 row)
postgres=# select * from pg_stat_checkpointer;
num_timed | num_requested | restartpoints_timed | restartpoints_req | restartpoints_done | write_time | sync_time | buffers_written | stats_reset
-----------+---------------+---------------------+-------------------+--------------------+------------+-----------+-----------------+-------------------------------
1 | 1 | 0 | 0 | 0 | 4737 | 103 | 13358 | 2024-11-25 13:23:41.271027+00
(1 row)
İşlem sonucunda background writer process’inin devreye girdiğini ve shared buffers alanından 4466 “dirty page” i diske yazarak boş yer açtığını görebiliyoruz. Bu yazma işlemlerinin 20 tanesi “bgwriter_lru_maxpages” değerinden fazla page’i yazdığı için kesilmiş (maxwritten_clean=20). Bu değer test ortamımızda varsayılan olarak 100 idi, eğer pg_stat_bgwriter view çıktısında maxwritten_clean kolonunun devamlı yükseldiği gözlemleniyorsa “bgwriter_lru_maxpages” parametresini arttırmak değerlendirilebilir. pg_stat_checkpointer çıktısında ise test esnasında 1 otomatik checkpoint alındığını ancak bu checkpoint ile çok az page yazıldığını görüyoruz. Bu checkpoint’in bizim insert işlemimizden önce tamamlanmış olduğunu söyleyebiliriz.
Şimdi, bir otomatik checkpoint daha alınması için 5 dk’dan (checkpoint_timeout) biraz daha fazla bekledikten sonra view’ları tekrar sorguluyoruz.
postgres=# select * from pg_stat_bgwriter;
buffers_clean | maxwritten_clean | buffers_alloc | stats_reset
---------------+------------------+---------------+-------------------------------
4837 | 26 | 27669 | 2024-11-11 09:50:11.810176+00
(1 row)
postgres=# select * from pg_stat_checkpointer;
num_timed | num_requested | restartpoints_timed | restartpoints_req | restartpoints_done | write_time | sync_time | buffers_written | stats_reset
-----------+---------------+---------------------+-------------------+--------------------+------------+-----------+-----------------+-------------------------------
2 | 1 | 0 | 0 | 0 | 6184 | 121 | 19140 | 2024-11-25 13:23:41.271027+00
(1 row)
Son insert/select işlemimizden kaynaklanan dirty page’lerin bir kısmını bgwriter temizlenmişti, kalanının da otomatik checkpoint ile temizlendiğini pg_stat_checkpointer view çıktısından görebiliyoruz. Checkpointer ve background writer process’lerinin istatistiklerini görebileceğimiz bir diğer view da pg_stat_io. Son olarak aşağıda da testlerimizden sonra pg_stat_io çıktısını görüyoruz.
postgres=# select backend_type,object,context,reads,writes,writebacks,extends,op_bytes,hits,evictions,reuses,fsyncs
from pg_stat_io WHERE reads <> 0 OR writes <> 0;
backend_type | object | context | reads | writes | writebacks | extends | op_bytes | hits | evictions | reuses | fsyncs
--------------------+----------+----------+-------+--------+------------+---------+----------+---------+-----------+--------+--------
client backend | relation | bulkread | 7368 | 0 | 0 | | 8192 | 5907 | 32 | 7336 |
client backend | relation | normal | 325 | 3 | 0 | 26560 | 8192 | 6056898 | 10764 | | 0
autovacuum worker | relation | normal | 246 | 0 | 0 | 11 | 8192 | 35912 | 128 | | 0
autovacuum worker | relation | vacuum | 10994 | 320 | 0 | 0 | 8192 | 55639 | 256 | 10605 |
standalone backend | relation | normal | 537 | 1010 | 1010 | 661 | 8192 | 86875 | 0 | | 0
standalone backend | relation | vacuum | 9 | 0 | 0 | 0 | 8192 | 906 | 0 | 0 |
background writer | relation | normal | | 5034 | 4992 | | 8192 | | | | 0
checkpointer | relation | normal | | 19431 | 19406 | | 8192 | | | | 40
(8 rows)
Checkpoint işlemi veri tabanı performansı açısından çok önemli bir faktör ve yoğun veri tabanlarında checkpoint, background write işlemlerinin doğru şekilde tune edilmesi çok önemli. Bununla ilgili sorularınız olursa, bizimle iletişime geçebilirsiniz.

Bir yanıt yazın