PostgreSQL'de streaming replication, birincil (primary) sunucunun ürettiği WAL kayıtlarını ağ üzerinden anlık olarak bir veya daha fazla bekleme (standby) sunucusuna aktarmasıdır. Kurmak için birincilde REPLICATION yetkili bir rol ve pg_hba.conf izni yeterlidir; bekleme sunucusunu tek komutla, pg_basebackup -R -X stream ile oluşturursunuz. -R seçeneği standby.signal dosyasını ve primary_conninfo ayarını sizin yerinize yazar; sunucuyu başlattığınızda WAL akışı başlar ve bekleme sunucusu salt okunur sorgu kabul eder.
Bu yazıda birincil ve bekleme sunucusunu PostgreSQL 18.1 üzerinde aynı makinede iki ayrı portta kurduk: replikasyon slotu oluşturduk, pg_stat_replication ile gecikmeyi ölçtük, bekleme sunucusunda yazmanın neden reddedildiğini gördük, slotun bekleme sunucusu kapalıyken WAL biriktirmesini izledik ve sonunda pg_ctl promote ile bekleme sunucusunu birincile çevirdik. Senkron replikasyon, hot standby sorgu çakışmaları ve failover sırasında dikkat edilecekler de anlatılıyor. Tek tablo veya satır düzeyinde seçici aktarma istiyorsanız logical replication yazısına bakın.
Fiziksel replikasyon nasıl çalışır?

Bekleme sunucusu aslında sürekli kurtarma modunda çalışan bir PostgreSQL kümesidir. Birincilden aldığı WAL kayıtlarını kendi veri dosyalarına uygular; bu yüzden birincilin bayt düzeyinde bir kopyası olur. Kılavuz iki aktarım biçimini anlatıyor. Dosya tabanlı günlük gönderiminde (log shipping) 16 MB'lık WAL segmentleri dolup arşivlendikçe bekleme sunucusu onları restore_command ile alır. Streaming replication'da ise bekleme sunucusu birincile TCP ile bağlanır ve WAL kayıtlarını segmentin dolmasını beklemeden, üretildikçe alır. Kılavuza göre bu gecikme, bekleme sunucusu yüke yetişebiliyorsa genellikle bir saniyenin altındadır.
Bekleme sunucusu açılışta önce arşivde bulunan WAL'ı uygular, sonra kendi pg_wal klasörüne bakar, ardından primary_conninfo tanımlıysa birincile bağlanıp akışa geçer. Bağlantı koparsa bu döngüye geri döner. Bağlantı kurulduğunda bekleme tarafında bir walreceiver, birincil tarafında da ona karşılık gelen bir walsender süreci görürsünüz.
Fiziksel replikasyon bütün kümeyi kopyalar; tek veritabanı veya tablo seçemezsiniz. Birincil ile bekleme sunucusu aynı ana sürümde olmalıdır. Kılavuz farklı küçük sürümlerin büyük ihtimalle birlikte çalışacağını ama bunun resmî olarak desteklenmediğini, küçük sürüm güncellemesinde önce bekleme sunucularının güncellenmesini öneriyor. Tablespace kullanıyorsanız bağlama yolları her iki tarafta aynı olmalıdır.
| Özellik | Fiziksel (streaming) | Mantıksal (logical) |
|---|---|---|
| Kopyalanan | Bütün küme, bayt düzeyinde | Seçilen tablolar, satır değişiklikleri |
| Hedef sunucu | Salt okunur bekleme sunucusu | Yazılabilir, kendi tabloları olabilir |
| Sürüm | Aynı ana sürüm | Farklı ana sürümler arasında da kullanılabilir |
| DDL | Otomatik gelir | Kopyalanmaz |
| Tipik kullanım | Yüksek erişilebilirlik, okuma ölçekleme | Veri dağıtımı, sürüm geçişi |
Birincil sunucuyu hazırlamak
Birincilde gereken ayarların çoğu PostgreSQL 18'de zaten varsayılan olarak açıktır: wal_level varsayılanı replica, max_wal_senders varsayılanı 10, max_replication_slots varsayılanı da 10'dur. Bekleme sunucusu başka bir makinedeyse listen_addresses ile birincilin o ağ arabiriminde dinlemesi gerekir (ayrıntılar pg_hba.conf ve uzak bağlantı yazısında). Ardından replikasyon için ayrı bir rol oluşturursunuz:
CREATE ROLE replikator WITH REPLICATION LOGIN PASSWORD 'gizli-bir-parola';Kılavuz replikasyon için süper kullanıcı yerine yalnızca REPLICATION ve LOGIN yetkili, ayrı bir hesap öneriyor. REPLICATION yetkisi WAL akışının tamamını okumaya izin verdiği için çok güçlüdür, ama süper kullanıcıdan farklı olarak birincildeki veriyi değiştirmeye izin vermez. WAL akışından ayrıcalıklı bilgiler kolayca çıkarılabileceği için bu hesabı yalnızca güvendiğiniz sunuculara açın.
pg_hba.conf'ta fiziksel replikasyon bağlantıları, veritabanı sütununa replication yazılan satırlarla yönetilir. Kılavuzdaki örneğe benzer bir satır:
# TYPE DATABASE USER ADDRESS METHOD
host replication replikator 192.168.1.100/32 scram-sha-256Bunu sınamak için test kümemizde replication satırlarını geçici olarak silip yalnızca host all all 127.0.0.1/32 satırını bıraktık; pg_basebackup hemen reddedildi. Yani all satırı replikasyon bağlantısını kapsamadı:
pg_basebackup: error: connection to server at "127.0.0.1", port 46470 failed: FATAL: no pg_hba.conf entry for replication connection from host "127.0.0.1", user "replikator", no encryptionBiz testi tek makinede, yalnızca 127.0.0.1'de dinleyen ve initdb -A trust ile kurulmuş geçici kümelerde yaptık; initdb'nin oluşturduğu pg_hba.conf zaten host replication all 127.0.0.1/32 trust satırını içeriyordu. Gerçek sunucularda trust kullanmayın; parola doğrulaması için scram-sha-256 seçin ve parolayı bekleme sunucusunda primary_conninfo içine ya da .pgpass dosyasına (veritabanı alanına replication yazarak) koyun. Bağlantıyı şifrelemek için SSL/TLS yazısına bakabilirsiniz.
Bekleme sunucusunu pg_basebackup -R ile oluşturmak
pg_basebackup -h 127.0.0.1 -p 46470 -U replikator -D yedek -R -X stream -C -S yedek_slot
echo $?
# 0
ls yedek | grep signal
# standby.signalBu tek komut birkaç işi birlikte yapar. -D yedek birincilin veri dizinini boş bir klasöre kopyalar. -X stream yedek sürerken üretilen WAL'ı ikinci bir bağlantıdan aktarır. -C -S yedek_slot birincilde yedek_slot adlı bir fiziksel replikasyon slotu oluşturup yedek için kullanır. -R ise veri dizinine standby.signal dosyasını koyar ve bağlantı ayarlarını postgresql.auto.conf dosyasına ekler:
primary_conninfo = 'user=replikator passfile=... channel_binding=prefer host=127.0.0.1 port=46470 ...'
primary_slot_name = 'yedek_slot'(Satır uzun olduğu için kısalttık; pg_basebackup yedek sırasında kullandığı bütün bağlantı seçeneklerini yazıyor.) postgresql.auto.conf dosyası ALTER SYSTEM tarafından yönetilir ve elle düzenlenmemesi gerektiğini belirten bir başlık içerir. Kopyanın postgresql.conf dosyası birincilinkiyle aynı olduğu için, aynı makinede çalıştırdığımız bu testte yalnızca port değerini 46471 yaptık ve birincilden gelen archive_mode ayarını kapattık. Ardından sunucuyu başlattık:
pg_ctl -D yedek -l yedek.log -w startLOG: entering standby mode
LOG: consistent recovery state reached at 0/C000158
LOG: database system is ready to accept read-only connections
LOG: started streaming WAL from primary at 0/D000000 on timeline 1Bekleme modunu başlatan şey standby.signal dosyasıdır. Tutarlı duruma ulaşıldığı anda sunucu salt okunur bağlantı kabul etmeye başladı; bunu sağlayan hot_standby ayarı varsayılan olarak on'dur. Son satır, akışın birincilden başladığını gösteriyor.
pg_stat_replication ile durumu izlemek
Birincil tarafında her bağlı bekleme sunucusu için pg_stat_replication görünümünde bir satır bulunur:
SELECT pid, usename, application_name, client_addr, state, sent_lsn,
write_lsn, flush_lsn, replay_lsn, sync_state
FROM pg_stat_replication;-[ RECORD 1 ]----+------------
pid | 68056
usename | replikator
application_name | walreceiver
client_addr | 127.0.0.1
state | streaming
sent_lsn | 0/D000060
write_lsn | 0/D000060
flush_lsn | 0/D000060
replay_lsn | 0/D000060
sync_state | asyncDört LSN sütunu WAL'ın yolculuğunu adım adım anlatır: sent_lsn birincilin gönderdiği, write_lsn bekleme sunucusunun işletim sistemine yazdığı, flush_lsn diske kalıcı olarak yazdığı, replay_lsn ise veri dosyalarına uyguladığı son konumdur. state sütunu ilk bağlantıda catchup, birincile yetişince streaming olur. sync_state = async bu bekleme sunucusunun asenkron olduğunu gösterir. application_name varsayılan olarak walreceiver'dır; senkron replikasyonda bekleme sunucularını ayırt etmek için primary_conninfo içinde application_name vermeniz gerekir.
Gecikmeyi bayt ve süre olarak ölçmek için:
SELECT application_name, state,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS gecikme_bayt,
write_lag, replay_lag
FROM pg_stat_replication; application_name | state | gecikme_bayt | write_lag | replay_lag
------------------+-----------+--------------+-----------------+-----------------
walreceiver | streaming | 0 | 00:00:00.000266 | 00:00:00.000376
(1 row)Kılavuzun yorumu şöyle: pg_current_wal_lsn() ile sent_lsn arasındaki büyük fark birincilin yoğun yük altında olduğuna, sent_lsn ile bekleme tarafındaki pg_last_wal_receive_lsn() arasındaki fark ağ gecikmesine ya da bekleme sunucusunun yük altında olduğuna işaret eder. Bekleme tarafında alınan ama henüz uygulanmamış WAL'ı görmek için pg_last_wal_replay_lsn() ve pg_stat_wal_receiver görünümü kullanılır.
Bekleme sunucusunda sorgu çalıştırmak
Birincilde tabloya bir satır ekledikten hemen sonra bekleme sunucusuna bağlandık:
-- birincilde
INSERT INTO urun VALUES (3, 'Lokum kutusu', 60);
-- bekleme sunucusunda
SELECT pg_is_in_recovery(); -- sonuç: t
SHOW in_hot_standby; -- sonuç: on
SELECT * FROM urun ORDER BY id; id | ad | stok
----+--------------+------
1 | Çay bardağı | 120
2 | Türk kahvesi | 45
3 | Lokum kutusu | 60
(3 rows)SELECT status, sender_host, sender_port, slot_name FROM pg_stat_wal_receiver; status | sender_host | sender_port | slot_name
-----------+-------------+-------------+------------
streaming | 127.0.0.1 | 46470 | yedek_slot
(1 row)Yeni satır bekleme sunucusuna gelmişti. Aynı oturumda yazmayı denediğimizde ise:
INSERT INTO urun VALUES (4, 'Kolonya', 10);
-- ERROR: cannot execute INSERT in a read-only transactionHot standby oturumlarına transaction kimliği atanmaz ve bu oturumlar WAL yazamaz. Bu yüzden INSERT, UPDATE, DELETE, MERGE, COPY FROM, TRUNCATE, her türlü DDL (geçici tablolar dahil), SELECT ... FOR UPDATE, nextval(), LISTEN/NOTIFY ve iki aşamalı commit komutları reddedilir. SELECT, COPY TO, imleçler, SET/SHOW ve SAVEPOINT gibi transaction komutları ise çalışır. Uygulamanız okuma trafiğini bekleme sunucusuna yönlendiriyorsa, verinin birincilden biraz geride olabileceğini (kılavuzun deyişiyle "eninde sonunda tutarlı") hesaba katmalısınız.
Sorgu çakışmaları
Birincilde yapılan bazı işlemler bekleme sunucusundaki sorgularla çakışır: birincilde DROP TABLE ya da ACCESS EXCLUSIVE kilit isteyen DDL, veritabanı silme ve en sık olarak VACUUM'un, bekleme tarafındaki uzun bir sorgunun hâlâ görmesi gereken eski satır sürümlerini temizlemesi. Birincilde böyle bir çakışma sadece beklemeyle sonuçlanır; bekleme sunucusunda ise WAL'ın uygulanması zorunludur. PostgreSQL bir süre bekler, süre max_standby_streaming_delay (arşivden gelen WAL için max_standby_archive_delay) değerini aşarsa çakışan sorguyu iptal eder.
Yüksek erişilebilirlik için kullanılan bir bekleme sunucusunda bu gecikmeleri kısa tutmak, sunucunun birincilin çok gerisine düşmemesini sağlar. Uzun raporlar için kullanılan bir bekleme sunucusunda ise daha yüksek değerler seçilebilir. Diğer bir çözüm hot_standby_feedback = on'dur: bekleme sunucusu çalışan sorguları birincile bildirir ve VACUUM ilgili satırları silmez. Bedeli, birincilde ölü satırların daha geç temizlenmesi ve tabloların şişebilmesidir (VACUUM ve bloat).
Replikasyon slotları ve WAL birikmesi

Slot olmadan birincil, bekleme sunucusunun henüz almadığı WAL segmentlerini geri dönüştürebilir; bu olursa bekleme sunucusu yeni bir temel yedekle yeniden kurulmak zorunda kalır. Bunu önlemenin üç yolu var: wal_keep_size ile belirli miktarda WAL'ı saklamak, bekleme sunucusunun erişebildiği bir WAL arşivi kullanmak ya da replikasyon slotu. Slot, yalnızca gerçekten gereken segmentleri tuttuğu için en hassas yöntemdir. Riskini görmek için bekleme sunucusunu durdurduk, birincilde 200 bin satırlık bir tablo oluşturduk ve slota baktık:
SELECT slot_name, active, wal_status,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS tutulan_wal
FROM pg_replication_slots; slot_name | active | wal_status | tutulan_wal
------------+--------+------------+-------------
yedek_slot | f | reserved | 32 MB
(1 row)Bekleme sunucusu kapalı olduğu halde birincil 32 MB WAL'ı silmeden tuttu. Bekleme sunucusunu tekrar başlattığımızda birkaç saniye içinde yetişti ve değer 0 bytes'a döndü. Ama bekleme sunucusu hiç geri gelmezse slot WAL'ı süresiz biriktirir ve pg_wal diski dolabilir. Kılavuz bu konuda açıkça uyarıyor. Korunma yolları:
max_slot_wal_keep_size: slotların tutabileceği WAL miktarına üst sınır koyar; varsayılan -1, yani sınırsızdır. Sınır aşılırsa bekleme sunucusu replikasyona devam edemeyebilir.idle_replication_slot_timeout: PostgreSQL: Release 18 notlarına göre PostgreSQL 18 ile eklendi; belirtilen süreden uzun süre kullanılmayan slotları geçersiz kılar. Varsayılan 0, yani kapalıdır.- Artık kullanılmayan slotu silmek:
SELECT pg_drop_replication_slot('yedek_slot'); pg_replication_slots.wal_statusveactivesütunlarını düzenli izlemek.
Senkron replikasyon
Streaming replication varsayılan olarak asenkrondur: birincil commit'i onayladıktan sonra WAL bekleme sunucusuna gider. Birincil o arada çökerse, henüz gönderilmemiş transaction'lar kaybolabilir. Senkron replikasyonda ise commit, WAL'ın hem birincilde hem de senkron bekleme sunucusunda diske yazıldığı onaylanana kadar bekler. Açmak için birincilde synchronous_standby_names ayarını boş olmayan bir değere getirmek yeterlidir; synchronous_commit varsayılanı zaten on'dur.
synchronous_standby_names = 'FIRST 2 (s1, s2, s3)' # öncelik sırasına göre 2 bekleme sunucusu
synchronous_standby_names = 'ANY 2 (s1, s2, s3)' # listeden herhangi 2'si (yeter sayı)synchronous_commit değeri kullanıcı, veritabanı ya da tek transaction düzeyinde değiştirilebilir. remote_write bekleme sunucusunun veriyi işletim sistemine yazmasını beklemekle yetinir; remote_apply ise verinin bekleme sunucusunda uygulanıp sorgularda görünür olmasını bekler. Kılavuzun örneğindeki gibi, önemli müşteri verisi için senkron, sohbet mesajları gibi kaybı tolere edilebilir veri için asenkron commit kullanmak yaygın bir dengedir. Dikkat: senkron listede adı geçen bekleme sunucuları çökerse ve yerine geçecek aday kalmazsa, commit'ler sonsuza kadar bekleyebilir. Bu yüzden senkron kurulumlarda en az bir yedek aday adlandırmak önerilir.
Failover: bekleme sunucusunu birincile yükseltmek
Birincil çöktüğünde bekleme sunucusunu birincile çevirmek için pg_ctl promote komutunu ya da pg_promote() fonksiyonunu kullanırsınız:
pg_ctl -D yedek promote
# waiting for server to promote.... done
# server promotedLOG: selected new timeline ID: 2
LOG: archive recovery complete
LOG: database system is ready to accept connectionsYükseltme sonrası pg_is_in_recovery() f döndürdü ve sunucu yazma kabul etmeye başladı. Yeni bir zaman çizelgesi (timeline 2) açıldı; zaman çizelgeleri WAL arşivleme ve PITR yazısında anlatılıyor. Birden fazla bekleme sunucunuz varsa, diğerlerinin yeni birincili izleyebilmesi için recovery_target_timeline ayarının varsayılan latest değerinde kalması gerekir.
PostgreSQL birincilin çöktüğünü kendisi algılayıp otomatik failover yapmaz; kılavuz bunun için işletim sistemiyle bütünleşen ayrı araçlar gerektiğini söylüyor. Ayrıca eski birincil yeniden açılırsa ona artık birincil olmadığını bildiren bir mekanizma şarttır; aksi halde iki sunucu da kendini birincil sanar ve veri kaybı yaşanır. Failover sonrası sistem tek sunucuyla çalışır; yeni bir bekleme sunucusu kurulana kadar bu durumu geçici sayın. Eski birincili yeniden bekleme sunucusu yapmak için yeni bir temel yedek alabilir veya büyük kümelerde pg_rewind aracından yararlanabilirsiniz. Son olarak, eski birincildeki artık kullanılmayan slotu silmeyi unutmayın; biz testin sonunda pg_drop_replication_slot('yedek_slot') çalıştırdık.
Sık yapılan hatalar

pg_hba.conf'ta veritabanı sütununaallyazıp replikasyonun çalışmasını beklemek; replikasyon bağlantıları içinreplicationanahtar kelimesi gerekir.- Bekleme sunucusu silindiği veya uzun süre kapalı kaldığı halde slotunu bırakmak; birincilin diski WAL ile dolar.
- Bekleme sunucusunu yedek yerine koymak. Birincilde yanlışlıkla silinen veri birkaç milisaniye içinde bekleme sunucusunda da silinir; geri dönüş için pg_dump veya PITR gerekir.
- Aynı makinede deneme yaparken kopyanın portunu değiştirmemek; iki sunucu aynı portu kullanamaz.
- Senkron replikasyonda tek bir senkron bekleme sunucusuna güvenmek; o sunucu durursa birincildeki yazmalar bekler.
- Hot standby'da uzun raporların iptal edilmesine şaşırmak;
max_standby_streaming_delayvehot_standby_feedbackayarlarını iş yüküne göre seçin.
Bekleme sunucusu başlamazsa ilk bakılacak yer sunucu günlüğüdür; bağlantı reddi, kimlik doğrulama hatası veya eksik WAL burada açıkça yazar. Bağlantı hatalarının çözümü için bağlantı hataları, genel izleme için pg_stat_activity ve pg_stat_statements yazılarına bakabilirsiniz. Linux sunucularda PostgreSQL servisinin systemd ile yönetimi için systemd servis yönetimi rehberi işinize yarar.
Sık Sorulan Sorular
Bekleme sunucusu yedek yerine geçer mi?
Hayır. Replikasyon birincildeki her değişikliği, hatalı DELETE veya DROP komutları dahil, bekleme sunucusuna taşır. Yedek, geçmişe dönebilmeniz için ayrı tutulmalıdır.
Slot kullanmak zorunlu mu?
Zorunlu değil. WAL arşivi veya wal_keep_size da bekleme sunucusunun eksik WAL yüzünden kopmasını önleyebilir. Slot daha hassastır, ama kullanılmayan slot birincilde WAL biriktirir; izlemeyi ve gerekirse max_slot_wal_keep_size sınırını unutmayın.
Bekleme sunucusunda geçici tablo oluşturabilir miyim?
Hayır. Kılavuza göre geçici tablolar da dahil DDL ve DML komutları hot standby'da reddedilir, çünkü bunlar transaction kimliği ve sistem kataloğu güncellemesi gerektirir.
Farklı ana sürümler arasında streaming replication kurulabilir mi?
Hayır, fiziksel replikasyon aynı ana sürüm gerektirir. Farklı ana sürümler arasında veri aktarmak için logical replication veya pg_upgrade kullanılır.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: PostgreSQL öğrenme rehberi · Logical replication · WAL arşivleme ve PITR · pg_hba.conf · İzleme görünümleri · Rol yönetimi