~/icsd.ir — bash
SYSTEM_ONLINE

Replication و Streaming در PostgreSQL

Replication یعنی نگهداری چند نسخه از دیتابیس روی سرورهای مختلف. PostgreSQL دو نوع replication دارد: Physical (Streaming) برای کپی کامل و Logical برای کپی انتخابی. در این فصل با هر دو، به‌علاوه HA با Patroni و pgpool آشنا می‌شویم.

Replication یعنی نگهداری چند نسخه از دیتابیس روی سرورهای مختلف. PostgreSQL دو نوع replication دارد: Physical (Streaming) برای کپی کامل و Logical برای کپی انتخابی. در این فصل با هر دو، به‌علاوه HA با Patroni و pgpool آشنا می‌شویم.

چرا Replication؟

  • High Availability: اگر primary سقوط کند، standby ادامه می‌دهد
  • Load Balancing: read query‌ها روی replica‌ها
  • Disaster Recovery: کپی در data center دیگر
  • Geographical distribution: replica نزدیک کاربر
  • Reporting: query سنگین روی replica، بدون تاثیر روی primary

انواع Replication

ویژگی Physical (Streaming) Logical
سطح byte-level (WAL) row-level
کپی کل cluster جدول‌های انتخابی
نسخه PG باید یکسان می‌تواند متفاوت
standby می‌نویسد؟ خیر (read-only) بله
کاربرد HA، DR، read scaling upgrade، sharding، sync انتخابی

Streaming Replication – راه‌اندازی

روی Primary

# postgresql.conf
listen_addresses = '*'
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1GB                      # یا max_wal_size در PG 13+
hot_standby = on
# pg_hba.conf - اجازه اتصال replication از standby
host    replication    replicator    10.0.0.20/32    scram-sha-256
-- ساخت user مخصوص replication
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'StrongPass!';

روی Standby

# متوقف کردن standby
sudo systemctl stop postgresql

# پاک کردن data directory
sudo rm -rf /var/lib/postgresql/16/main/*

# گرفتن base backup از primary
sudo -u postgres pg_basebackup 
    -h 10.0.0.10 
    -U replicator 
    -D /var/lib/postgresql/16/main 
    -P -v -R                              # -R = standby.signal و primary_conninfo بسازد

# -R خودکار این فایل‌ها را می‌سازد:
# /var/lib/postgresql/16/main/standby.signal     (نشانه standby بودن)
# و در postgresql.auto.conf:
# primary_conninfo = 'host=10.0.0.10 user=replicator password=...'

# شروع
sudo systemctl start postgresql

تست

-- روی Primary
SELECT * FROM pg_stat_replication;
-- application_name | client_addr | state     | sent_lsn  | replay_lsn | replay_lag
-- walreceiver      | 10.0.0.20   | streaming | 0/3000148 | 0/3000148  | 00:00:00

-- روی Standby
SELECT pg_is_in_recovery();        -- t (true)

-- query کار می‌کند:
SELECT count(*) FROM products;
-- اما نمی‌توان نوشت:
INSERT INTO products ...;
-- ERROR: cannot execute INSERT in a read-only transaction

Synchronous vs Asynchronous

Asynchronous (پیش‌فرض)

primary منتظر standby نیست. سریع‌تر، اما ریسک از دست دادن داده در failure.

Synchronous

# postgresql.conf روی primary
synchronous_commit = on
synchronous_standby_names = 'standby1, standby2'
# یا برای any one:
synchronous_standby_names = 'ANY 1 (standby1, standby2)'

هر COMMIT منتظر می‌ماند تا standby دریافت کند → durability بالاتر، latency بیشتر.

Cascading Replication

Primary
   │
   ↓
Standby 1
   │
   ↓
Standby 2 (replicate from Standby 1)

مفید برای کاهش بار روی primary وقتی چند standby داریم.

Logical Replication

کپی row-level بر اساس PUBLICATION/SUBSCRIPTION:

روی Source (Publisher)

# postgresql.conf
wal_level = logical
max_wal_senders = 10
max_replication_slots = 10
-- ساخت publication
CREATE PUBLICATION my_pub FOR TABLE products, orders;

-- یا همه جدول‌ها
CREATE PUBLICATION my_pub_all FOR ALL TABLES;

-- یا فقط INSERT و UPDATE
CREATE PUBLICATION my_pub_iu FOR TABLE products
    WITH (publish = 'insert, update');

-- لیست
SELECT * FROM pg_publication;

روی Target (Subscriber)

-- جدول‌ها باید قبلاً وجود داشته باشند با همان schema
CREATE TABLE products (...);
CREATE TABLE orders (...);

-- ساخت subscription
CREATE SUBSCRIPTION my_sub
    CONNECTION 'host=10.0.0.10 dbname=shopdb user=replicator password=...'
    PUBLICATION my_pub;

-- داده اولیه کپی می‌شود، سپس تغییرات stream می‌شوند

-- بررسی
SELECT * FROM pg_stat_subscription;

کاربردهای Logical Replication

  • upgrade: PG 14 → PG 16 با downtime حداقل
  • cross-version: replication بین نسخه‌های مختلف
  • partial sync: فقط جدول‌های خاص
  • combine sources: داده از چند DB در یک DB جمع شود
  • ETL: انتقال داده real-time به analytical DB

Failover – تعویض اضطراری

وقتی primary سقوط می‌کند، باید standby را به primary تبدیل کنیم:

# روی standby - تبدیل به primary
pg_ctl promote -D /var/lib/postgresql/16/main

# یا با SQL (PG 12+)
psql -c "SELECT pg_promote();"

# حالا standby می‌تواند بنویسد
# باید app را به IP جدید point کنید

چالش‌های Failover دستی

  • تشخیص زمان دقیق failure
  • اطمینان از sync بودن standby
  • تغییر connection string در app
  • تبدیل primary سابق به standby

راه‌حل: Patroni یا ابزارهای HA دیگر.

Patroni – HA خودکار

Patroni با etcd/Consul/ZooKeeper برای coordination، failover خودکار را مدیریت می‌کند.

┌──────────┐     ┌──────────┐     ┌──────────┐
│ Patroni  │     │ Patroni  │     │ Patroni  │
│ Primary  │     │ Standby  │     │ Standby  │
└────┬─────┘     └────┬─────┘     └────┬─────┘
     │                │                │
     └────────────────┼────────────────┘
                      │
              ┌───────▼────────┐
              │ etcd / Consul  │
              │ (coordination) │
              └────────────────┘

نصب Patroni

sudo apt install patroni etcd
# /etc/patroni/config.yml
scope: pgcluster
namespace: /service/
name: node1

restapi:
  listen: 0.0.0.0:8008
  connect_address: 10.0.0.10:8008

etcd:
  hosts: 10.0.0.30:2379,10.0.0.31:2379,10.0.0.32:2379

bootstrap:
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576
    postgresql:
      use_pg_rewind: true
      parameters:
        wal_level: replica
        hot_standby: "on"
        max_wal_senders: 10
        max_replication_slots: 10

postgresql:
  listen: 0.0.0.0:5432
  connect_address: 10.0.0.10:5432
  data_dir: /var/lib/postgresql/16/main
  authentication:
    superuser:
      username: postgres
      password: secret
    replication:
      username: replicator
      password: replpass

قابلیت‌های Patroni

  • failover خودکار
  • switchover (تعویض با برنامه)
  • اضافه/کم کردن node خودکار
  • REST API برای monitoring
  • integration با HAProxy/pgBouncer

pgpool-II – Connection Routing

pgpool-II این قابلیت‌ها را دارد:

  • Connection pooling
  • Load balancing (write به primary، read به standby‌ها)
  • Auto failover detection
  • Query caching
sudo apt install pgpool2
# pgpool.conf
backend_hostname0 = '10.0.0.10'
backend_port0 = 5432
backend_weight0 = 1
backend_flag0 = 'ALLOW_TO_FAILOVER'

backend_hostname1 = '10.0.0.20'
backend_port1 = 5432
backend_weight1 = 1
backend_flag1 = 'ALLOW_TO_FAILOVER'

load_balance_mode = on
master_slave_mode = on
master_slave_sub_mode = 'stream'

monitoring Replication

-- روی Primary
SELECT 
    application_name, client_addr, state,
    pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) AS lag,
    sync_state
FROM pg_stat_replication;

-- روی Standby - تأخیر replication
SELECT 
    pg_is_in_recovery(),
    pg_last_wal_receive_lsn(),
    pg_last_wal_replay_lsn(),
    EXTRACT(EPOCH FROM (NOW() - pg_last_xact_replay_timestamp())) AS lag_seconds;

-- alerting بر اساس lag > 60s
-- در Prometheus + Grafana

Replication Slots

slot تضمین می‌کند WAL تا زمانی که standby آن را دریافت نکرده، حذف نشود:

-- ساخت slot برای standby
SELECT pg_create_physical_replication_slot('standby1_slot');

-- در standby، primary_conninfo:
-- primary_slot_name = 'standby1_slot'

-- لیست slots
SELECT slot_name, active, restart_lsn, 
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag
FROM pg_replication_slots;

-- هشدار: slot غیرفعال می‌تواند باعث پر شدن دیسک شود!
-- اگر standby آفلاین برای مدت طولانی، slot را حذف کنید:
SELECT pg_drop_replication_slot('standby1_slot');

بهترین شیوه‌ها

  • برای HA: حداقل ۳ node با Patroni
  • standby در data center جداگانه برای DR
  • monitoring lag (alerting برای lag > 30s)
  • replication slot با احتیاط (می‌تواند disk را پر کند)
  • synchronous replication فقط اگر latency اجازه می‌دهد
  • test کردن failover به‌صورت دوره‌ای
  • backup هم داشته باشید (replication برای backup نیست!)

جمع‌بندی

  • Streaming Replication: کپی WAL، کل cluster
  • Logical Replication: row-level، انتخابی، cross-version
  • Synchronous برای durability، Asynchronous برای performance
  • Patroni برای failover خودکار
  • pgpool-II برای routing و load balancing
  • Replication slots برای حفظ WAL
  • monitoring replication lag حیاتی است

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.