یک سرور کافی نیست
Replication دو هدف دارد: دسترسپذیری (اگر سرور اصلی سوخت، یکی دیگر جایش بنشیند) و تقسیم بار (گزارشهای سنگین روی نسخهی خواندنی). پستگرس دو نوع کاملاً متفاوت دارد.
| Streaming (فیزیکی) | Logical | |
|---|---|---|
| چه چیزی منتقل میشود | بایتهای WAL؛ کپی دقیق کل کلاستر | تغییرات ردیفی جدولهای انتخابی |
| نسخهی مقصد | باید همان نسخهی اصلی باشد | میتواند متفاوت باشد (ابزار ارتقای بیتوقف) |
| مقصد قابل نوشتن؟ | خیر، فقط خواندن | بله |
| DDL و sequence | بله، همهچیز | خیر؛ باید دستی هماهنگ شود |
| کاربرد | standby برای failover و خواندن | همگامسازی بخشی از داده، ارتقا، تجمیع |
راهاندازی Streaming Replication
-- روی سرور اصلی
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'رمز-تکثیر';
# pg_hba.conf روی سرور اصلی
hostssl replication replicator 10.0.0.6/32 scram-sha-256
# روی سرور standby (پوشهی داده خالی)
sudo -u postgres pg_basebackup -h 10.0.0.5 -U replicator \
-D /var/lib/postgresql/16/main -X stream -P -R -C -S standby_tehran
sudo systemctl start postgresql
-R فایل standby.signal و تنظیم primary_conninfo را خودکار مینویسد؛ -C -S یک replication slot میسازد تا سرور اصلی WAL موردنیاز standby را تا رسیدنش نگه دارد.
-- روی سرور اصلی: وضعیت و تأخیر
SELECT application_name, state, sync_state,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS lag_bytes,
replay_lag
FROM pg_stat_replication;
-- روی standby: ارتقا به اصلی هنگام خرابی
SELECT pg_promote();
Replication بهطور پیشفرض ناهمگام است: COMMIT منتظر standby نمیماند و در لحظهی خرابی ممکن است چند تراکنش آخر از دست برود. با synchronous_standby_names و synchronous_commit = on هیچ تراکنش تأییدشدهای گم نمیشود، به بهای تأخیر بیشتر در هر COMMIT. برای failover خودکار از ابزاری مثل Patroni استفاده کنید؛ promote دستی ساعت سه صبح قابل اتکا نیست.
Logical Replication
-- مبدأ (wal_level = logical)
CREATE PUBLICATION pub_sales FOR TABLE factory.orders, factory.order_items, factory.customers;
-- مقصد: ساختار جدولها باید از قبل وجود داشته باشد
CREATE SUBSCRIPTION sub_sales
CONNECTION 'host=10.0.0.5 dbname=carpet user=replicator password=...'
PUBLICATION pub_sales;
SELECT * FROM pg_stat_subscription;
نکتههایی که کمتر کسی میداند
- replication slot رهاشده (standby که خاموش شد و فراموشش کردید) WAL را تا بینهایت نگه میدارد و دیسک سرور اصلی را پر میکند؛
max_slot_wal_keep_size(نسخهی 13) سقف میگذارد. - جدولی که در Logical Replication شرکت دارد برای UPDATE و DELETE به کلید اصلی (یا REPLICA IDENTITY) نیاز دارد؛ بدون آن، UPDATE روی مبدأ خطا میدهد.
- بعد از سوییچ به سرور مقصد Logical، sequenceها عقباند؛ قبل از باز کردن نوشتن، همه را با setval جلو ببرید.
- کوئری طولانی روی standby ممکن است با خطای «canceling statement due to conflict with recovery» قطع شود؛
hot_standby_feedback = onاین را کم میکند اما باعث bloat روی سرور اصلی میشود. - نسخهی 17 ابزار
pg_createsubscriberرا آورد که یک standby فیزیکی را بدون کپی دوبارهی داده به subscriber منطقی تبدیل میکند؛ برای دیتابیسهای بزرگ ساعتها صرفهجویی است.