ลดความยุ่งยากในการใช้คลาวด์ พูดคุยกับเจ้าหน้าที่

ทำ Streaming Replication บน PostgreSQL
Home ทำ Streaming Replication บน PostgreSQL

ทำ Streaming Replication บน PostgreSQL

Streaming Replication ของ PostgreSQL ส่งข้อมูล WAL จากตัวหลักไปยังตัวสำรองแบบต่อเนื่อง ทำให้มีสำเนาที่ตามหลังเพียงเสี้ยววินาที ตัวสำรองยังรับงานอ่านได้ด้วย จึงช่วยแบ่งเบาภาระของตัวหลักไปพร้อมกัน

คู่มือนี้ตั้งค่าตั้งแต่ศูนย์ด้วย pg_basebackup ซึ่งเป็นวิธีมาตรฐานที่คัดลอกข้อมูลตั้งต้นและตั้งค่าให้พร้อมในคำสั่งเดียว

สิ่งที่ต้องเตรียม

  • เซิร์ฟเวอร์สองเครื่องที่ติดตั้ง PostgreSQL เวอร์ชันหลักเดียวกัน
  • เครือข่ายภายในระหว่างกัน และพื้นที่ดิสก์บนตัวสำรองไม่น้อยกว่าตัวหลัก

ในคู่มือนี้ตัวหลักคือ 10.0.0.41 และตัวสำรองคือ 10.0.0.42

ขั้นตอนที่ 1: ตั้งค่าตัวหลัก

sudo nano /etc/postgresql/16/main/postgresql.conf
listen_addresses = '10.0.0.41,localhost'
wal_level = replica
max_wal_senders = 10
wal_keep_size = 2GB
archive_mode = on
archive_command = 'test ! -f /var/lib/postgresql/archive/%f && cp %p /var/lib/postgresql/archive/%f'

# ให้ตัวสำรองรายงานตำแหน่งกลับมา ทำให้ดูความล่าช้าได้
hot_standby_feedback = on
sudo mkdir -p /var/lib/postgresql/archive
sudo chown postgres:postgres /var/lib/postgresql/archive

wal_keep_size คือปริมาณ WAL ที่เก็บไว้เผื่อตัวสำรองตามไม่ทัน หากตั้งน้อยเกินไปและตัวสำรองหลุดไปนาน จะต้องสร้างใหม่ทั้งหมด วิธีที่ดีกว่าคือใช้ Replication Slot ตามขั้นตอนที่ 3

ขั้นตอนที่ 2: สร้างผู้ใช้และอนุญาตการเชื่อมต่อ

sudo -u postgres psql
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'รหัสผ่านที่ยาวและสุ่ม';
SELECT rolname, rolreplication FROM pg_roles WHERE rolname = 'replicator';
sudo nano /etc/postgresql/16/main/pg_hba.conf
host    replication     replicator      10.0.0.42/32     scram-sha-256
sudo systemctl restart postgresql
sudo -u postgres psql -c "SHOW wal_level;"

ขั้นตอนที่ 3: สร้าง Replication Slot

Slot ทำให้ตัวหลักเก็บ WAL ไว้จนกว่าตัวสำรองจะรับไปแล้วจริง ป้องกันปัญหาตัวสำรองตามไม่ทันแล้วต้องสร้างใหม่

sudo -u postgres psql -c "SELECT pg_create_physical_replication_slot('standby1');"
sudo -u postgres psql -c "SELECT slot_name, active, restart_lsn FROM pg_replication_slots;"

ข้อควรระวัง หากตัวสำรองหายไปถาวรแต่ Slot ยังอยู่ ตัวหลักจะเก็บ WAL ไว้เรื่อย ๆ จนดิสก์เต็ม ต้องลบ Slot ทิ้งด้วย SELECT pg_drop_replication_slot('standby1');

ขั้นตอนที่ 4: สร้างตัวสำรองด้วย pg_basebackup

# บนตัวสำรอง หยุดบริการและล้างข้อมูลเดิม
sudo systemctl stop postgresql
sudo -u postgres rm -rf /var/lib/postgresql/16/main/*

sudo -u postgres pg_basebackup \
  -h 10.0.0.41 -p 5432 -U replicator \
  -D /var/lib/postgresql/16/main \
  -Fp -Xs -P -R \
  -S standby1

ความหมายของตัวเลือก -Fp คือเขียนเป็นไฟล์ปกติ -Xs คือรับ WAL ไปพร้อมกันระหว่างคัดลอก -P คือแสดงความคืบหน้า -R คือสร้างไฟล์ตั้งค่าการเชื่อมต่อให้อัตโนมัติ และ -S คือใช้ Slot ที่สร้างไว้

ตรวจว่าไฟล์ที่ต้องมีถูกสร้างแล้ว

sudo ls -l /var/lib/postgresql/16/main/standby.signal
sudo cat /var/lib/postgresql/16/main/postgresql.auto.conf

ขั้นตอนที่ 5: เริ่มตัวสำรอง

sudo nano /etc/postgresql/16/main/postgresql.conf
hot_standby = on
listen_addresses = '10.0.0.42,localhost'
sudo systemctl start postgresql
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"

ผลควรเป็น t ซึ่งแปลว่าอยู่ในโหมดตัวสำรองและกำลังรับข้อมูลอยู่

ขั้นตอนที่ 6: ตรวจสอบสถานะและความล่าช้า

-- บนตัวหลัก
SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn,
       write_lag, flush_lag, replay_lag, sync_state
FROM pg_stat_replication;
-- บนตัวสำรอง ดูว่าตามหลังกี่วินาที
SELECT CASE WHEN pg_last_wal_receive_lsn() = pg_last_wal_replay_lsn()
            THEN 0
            ELSE EXTRACT(EPOCH FROM now() - pg_last_xact_replay_timestamp())
       END AS lag_seconds;

ทดสอบจริง

# บนตัวหลัก
sudo -u postgres psql -c "CREATE TABLE IF NOT EXISTS repltest (id serial, t timestamptz DEFAULT now());"
sudo -u postgres psql -c "INSERT INTO repltest DEFAULT VALUES;"

# บนตัวสำรอง
sudo -u postgres psql -c "SELECT * FROM repltest ORDER BY id DESC LIMIT 5;"

ลองเขียนบนตัวสำรองเพื่อยืนยันว่าถูกปฏิเสธ

sudo -u postgres psql -c "INSERT INTO repltest DEFAULT VALUES;"
# ควรได้ ERROR: cannot execute INSERT in a read-only transaction

ขั้นตอนที่ 7: ใช้ตัวสำรองรับงานอ่าน

ชี้แอปส่วนที่อ่านอย่างเดียว เช่น หน้ารายงาน มาที่ตัวสำรอง ลดภาระตัวหลักได้มาก

ข้อควรระวังคือความล่าช้า ข้อมูลที่เพิ่งเขียนอาจยังไม่ปรากฏบนตัวสำรองทันที จึงไม่เหมาะกับหน้าที่ต้องอ่านข้อมูลที่เพิ่งบันทึกไป เช่น หน้ายืนยันคำสั่งซื้อ

หากต้องการให้ตัวสำรองยืนยันก่อนตัวหลักตอบว่าสำเร็จ ตั้งเป็นแบบซิงโครนัส

# บนตัวหลัก
synchronous_standby_names = 'standby1'
synchronous_commit = on

วิธีนี้ทำให้ไม่มีข้อมูลสูญหายเลยเมื่อตัวหลักล่ม แต่การเขียนจะช้าลงและหากตัวสำรองล่ม การเขียนบนตัวหลักจะหยุดรอ ต้องมีตัวสำรองอย่างน้อยสองตัวจึงจะใช้โหมดนี้ได้อย่างปลอดภัย

ขั้นตอนที่ 8: สลับตัวสำรองเป็นตัวหลัก

# บนตัวสำรอง
sudo -u postgres pg_ctl promote -D /var/lib/postgresql/16/main

# หรือ
sudo -u postgres psql -c "SELECT pg_promote();"

sudo -u postgres psql -c "SELECT pg_is_in_recovery();"   # ควรเป็น f

หลังสลับแล้ว เครื่องเดิมจะกลับมาเป็นตัวสำรองของเครื่องใหม่ไม่ได้ทันที ต้องใช้ pg_rewind ปรับให้ตรงกันก่อน

sudo systemctl stop postgresql
sudo -u postgres pg_rewind \
  --target-pgdata=/var/lib/postgresql/16/main \
  --source-server="host=10.0.0.42 user=replicator password=xxx" \
  --progress

ปัญหาที่พบบ่อย

pg_basebackup ล้มเหลวเรื่องการยืนยันตัวตน

ตรวจว่า pg_hba.conf มีบรรทัดสำหรับฐานข้อมูลชื่อ replication ซึ่งเป็นคำสงวน ไม่ใช่ชื่อฐานข้อมูลจริง และรีโหลดคอนฟิกแล้วด้วย sudo -u postgres psql -c "SELECT pg_reload_conf();"

ตัวสำรองตามไม่ทันจนหลุด

ใช้ Replication Slot ตามขั้นตอนที่ 3 และตรวจว่าดิสก์ของตัวสำรองไม่ช้ากว่าตัวหลัก ดูข้อความ requested WAL segment has already been removed ใน Log ซึ่งยืนยันอาการนี้

ดิสก์ตัวหลักเต็มเพราะ WAL

ตรวจ Slot ที่ไม่มีการใช้งานด้วย SELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) FROM pg_replication_slots; Slot ที่ active เป็น false และค้างมานานคือสาเหตุ ให้ลบทิ้ง

Query บนตัวสำรองถูกยกเลิก

เกิดเมื่อการเปลี่ยนแปลงจากตัวหลักขัดกับ Query ที่รันอยู่ ตั้ง max_standby_streaming_delay ให้ยาวขึ้น หรือเปิด hot_standby_feedback = on ซึ่งบอกตัวหลักไม่ให้ลบข้อมูลที่ตัวสำรองยังใช้อยู่

ต้องการวางระบบฐานข้อมูลพร้อม DR Site ในประเทศไทย ติดต่อ THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/

ผู้ให้บริการคลาวด์ไทย
เพื่อธุรกิจของคนไทย

"มุ่งมั่น" และ "มั่นคง"
พร้อมรับมือทุกการเติบโต
Trust Cloud
คลาว์ที่ปลอดภัย
คือรากฐานที่มั่นคง
cloud security