Hướng Dẫn Upgrade PostgreSQL 17.6 lên 18.1 (Chuẩn Production)

Hướng dẫn chi tiết upgrade PostgreSQL 17.6 lên 18.1 cho môi trường production với downtime tối thiểu. Bao gồm pg_upgrade, logical replication, rollback plan, và các best practices được cập nhật cho PostgreSQL 18 với Async I/O, Statistics Preservation, và pg_upgrade --swap mode mới.

Hướng Dẫn Upgrade PostgreSQL 17.6 lên 18.1 (Chuẩn Production)

PostgreSQL 18 chính thức ra mắt vào ngày 25/9/2025, đánh dấu một trong những bản release quan trọng nhất trong lịch sử PostgreSQL. Với việc giới thiệu Asynchronous I/O subsystem, hiệu năng đọc từ storage được cải thiện lên đến 3 lần, cùng nhiều tính năng mới như UUIDv7, Virtual Generated Columns, và OAuth 2.0 authentication.

Bài viết này sẽ hướng dẫn chi tiết cách upgrade từ PostgreSQL 17.6 lên 18.1 theo chuẩn production, đảm bảo downtime tối thiểurollback an toàn.


1. Tại Sao Nên Upgrade Lên PostgreSQL 18?

1.1. Các Tính Năng Nổi Bật

Tính năng Mô tả Lợi ích
Asynchronous I/O Hệ thống I/O bất đồng bộ mới Tăng tốc sequential scan, bitmap heap scan lên đến 3x
Statistics Preservation Giữ lại planner statistics khi upgrade Không cần chạy ANALYZE sau upgrade
Skip Scan Lookup trên multicolumn B-tree indexes Query nhanh hơn khi bỏ qua điều kiện = trên prefix columns
UUIDv7 Native uuidv7() function UUID có timestamp, tốt hơn cho indexing
Virtual Generated Columns Computed columns tại query time Linh hoạt hơn trong schema design
OAuth 2.0 Authentication với identity providers Tích hợp SSO dễ dàng
pg_upgrade --swap Chế độ swap mới Upgrade nhanh hơn, không cần copy files

1.2. Breaking Changes Cần Lưu Ý

⚠️ QUAN TRỌNG - Các thay đổi không tương thích ngược:

1. Data Checksums mặc định BẬT (initdb)
   - Cần matching checksum settings khi pg_upgrade
   
2. MD5 authentication DEPRECATED
   - Nên migrate sang SCRAM-SHA-256
   
3. Time zone abbreviation handling thay đổi
   - Session timezone được ưu tiên trước timezone_abbreviations

2. Chuẩn Bị Trước Khi Upgrade

2.1. Checklist Pre-Upgrade

#!/bin/bash
# pre-upgrade-checklist.sh

echo "=== PostgreSQL Upgrade Checklist ==="

# 1. Kiểm tra version hiện tại
echo "1. Current PostgreSQL Version:"
psql -c "SELECT version();"

# 2. Kiểm tra disk space
echo "2. Disk Space (cần ít nhất 2x data directory size):"
df -h /var/lib/postgresql

# 3. Kiểm tra data directory size
echo "3. Data Directory Size:"
du -sh /var/lib/postgresql/17/main

# 4. Kiểm tra checksum status
echo "4. Data Checksum Status:"
pg_controldata /var/lib/postgresql/17/main | grep "Data page checksum"

# 5. Kiểm tra extensions
echo "5. Installed Extensions:"
psql -c "SELECT extname, extversion FROM pg_extension ORDER BY extname;"

# 6. Kiểm tra replication slots
echo "6. Replication Slots:"
psql -c "SELECT slot_name, slot_type, active FROM pg_replication_slots;"

# 7. Kiểm tra prepared transactions
echo "7. Prepared Transactions (should be empty):"
psql -c "SELECT * FROM pg_prepared_xacts;"

2.2. Backup Strategy

QUAN TRỌNG: Luôn backup trước khi upgrade!

# 1. Full backup với pg_dumpall (logical backup)
pg_dumpall -U postgres -h localhost -f /backup/full_backup_$(date +%Y%m%d).sql

# 2. Base backup (physical backup) - khuyến nghị
pg_basebackup -D /backup/basebackup_$(date +%Y%m%d) \
  -Ft -z -P \
  -U replication \
  -h localhost

# 3. Backup configuration files
cp /etc/postgresql/17/main/postgresql.conf /backup/
cp /etc/postgresql/17/main/pg_hba.conf /backup/
cp /etc/postgresql/17/main/pg_ident.conf /backup/

2.3. Cài Đặt PostgreSQL 18.1

# Ubuntu/Debian
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get install postgresql-18

# RHEL/Rocky Linux
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf -qy module disable postgresql
sudo dnf install -y postgresql18-server postgresql18-contrib

3. Các Phương Pháp Upgrade

3.1. So Sánh Các Phương Pháp

Phương pháp Downtime Use Case Độ phức tạp
pg_upgrade Phút Same host, brief pause Thấp
pg_upgrade --link Giây - Phút Same filesystem, nhanh nhất Thấp
pg_upgrade --swap (PG18 mới) Giây - Phút Swap directories Thấp
Logical Replication Giây 24x7 apps, zero-downtime Cao
pg_dump/restore Giờ - Ngày Small DBs, cross-platform Thấp

3.2. Khuyến Nghị Cho Production

┌─────────────────────────────────────────────────────────────┐
│                    DECISION FLOWCHART                        │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Database size < 100GB?                                      │
│     YES → pg_upgrade --link hoặc --swap                     │
│     NO  ↓                                                   │
│                                                              │
│  Zero-downtime required?                                     │
│     YES → Logical Replication                                │
│     NO  → pg_upgrade --link với maintenance window           │
│                                                              │
│  Cross-platform migration?                                   │
│     YES → pg_dump/restore                                    │
│                                                              │
└─────────────────────────────────────────────────────────────┘

4.1. Khởi Tạo Cluster Mới

# Tạo data directory cho PostgreSQL 18
sudo mkdir -p /var/lib/postgresql/18/main
sudo chown postgres:postgres /var/lib/postgresql/18/main

# Chuyển sang user postgres
sudo -i -u postgres

# Kiểm tra checksum của cluster cũ
pg_controldata /var/lib/postgresql/17/main | grep "Data page checksum"
# Output: Data page checksum version: 0 (disabled) hoặc 1 (enabled)

# Init cluster mới với MATCHING checksum setting
# Nếu cluster cũ KHÔNG có checksum:
/usr/lib/postgresql/18/bin/initdb \
  -D /var/lib/postgresql/18/main \
  --no-data-checksums \
  --encoding=UTF8 \
  --locale=en_US.UTF-8

# Nếu cluster cũ CÓ checksum (hoặc muốn enable):
/usr/lib/postgresql/18/bin/initdb \
  -D /var/lib/postgresql/18/main \
  --data-checksums \
  --encoding=UTF8 \
  --locale=en_US.UTF-8

4.2. Pre-Upgrade Check

# Set environment variables
export PGBINOLD=/usr/lib/postgresql/17/bin
export PGBINNEW=/usr/lib/postgresql/18/bin
export PGDATAOLD=/var/lib/postgresql/17/main
export PGDATANEW=/var/lib/postgresql/18/main

# Chạy check mode TRƯỚC
/usr/lib/postgresql/18/bin/pg_upgrade \
  --old-datadir=$PGDATAOLD \
  --new-datadir=$PGDATANEW \
  --old-bindir=$PGBINOLD \
  --new-bindir=$PGBINNEW \
  --check

# Expected output:
# Performing Consistency Checks
# -----------------------------
# Checking cluster versions                         ok
# Checking database connection settings             ok
# Checking database user is the install user        ok
# Checking for prepared transactions                ok
# ...
# *Clusters are compatible*

4.3. Stop Services & Upgrade

# 1. Stop ứng dụng kết nối đến database
# (tùy thuộc vào setup của bạn)

# 2. Stop PostgreSQL 17
sudo systemctl stop postgresql@17-main

# 3. Verify PostgreSQL 17 đã stop
pg_isready -p 5432
# Output: no response

# 4. Thực hiện upgrade với --link mode (nhanh nhất)
cd /var/lib/postgresql
/usr/lib/postgresql/18/bin/pg_upgrade \
  --old-datadir=$PGDATAOLD \
  --new-datadir=$PGDATANEW \
  --old-bindir=$PGBINOLD \
  --new-bindir=$PGBINNEW \
  --link \
  --jobs=$(nproc)

# Hoặc với --swap mode (PostgreSQL 18 mới)
/usr/lib/postgresql/18/bin/pg_upgrade \
  --old-datadir=$PGDATAOLD \
  --new-datadir=$PGDATANEW \
  --old-bindir=$PGBINOLD \
  --new-bindir=$PGBINNEW \
  --swap \
  --jobs=$(nproc)

4.4. Copy Configuration Files

# Copy các config files từ cluster cũ
cp /etc/postgresql/17/main/postgresql.conf /etc/postgresql/18/main/
cp /etc/postgresql/17/main/pg_hba.conf /etc/postgresql/18/main/
cp /etc/postgresql/17/main/pg_ident.conf /etc/postgresql/18/main/

# Hoặc nếu dùng data directory cho config
cp $PGDATAOLD/postgresql.conf $PGDATANEW/
cp $PGDATAOLD/pg_hba.conf $PGDATANEW/

# Cập nhật port trong postgresql.conf nếu cần
# (nếu muốn chạy song song cả 2 version)
sed -i 's/port = 5432/port = 5433/' /etc/postgresql/18/main/postgresql.conf

4.5. Start PostgreSQL 18 & Verify

# Start PostgreSQL 18
sudo systemctl start postgresql@18-main

# Verify connection
psql -p 5432 -c "SELECT version();"
# Output: PostgreSQL 18.1 on x86_64-pc-linux-gnu...

# Verify databases
psql -c "\l"

# Verify table counts (sample)
psql -d your_database -c "SELECT schemaname, COUNT(*) FROM pg_tables GROUP BY schemaname;"

5. Post-Upgrade Tasks

5.1. Statistics Handling (PostgreSQL 18 tự động preserve)

# PostgreSQL 18 tự động preserve statistics!
# Chỉ cần chạy cho extended statistics nếu có:

/usr/lib/postgresql/18/bin/vacuumdb \
  --all \
  --analyze-in-stages \
  --missing-stats-only

# Hoặc chạy đầy đủ nếu muốn
vacuumdb --all --analyze

5.2. Extension Updates

# Kiểm tra extensions cần update
psql -c "SELECT * FROM pg_extension WHERE extversion != (SELECT default_version FROM pg_available_extensions WHERE name = extname);"

# Update tất cả extensions
psql -c "SELECT format('ALTER EXTENSION %I UPDATE;', extname) FROM pg_extension;" | psql

# Hoặc update từng extension
psql -c "ALTER EXTENSION pg_stat_statements UPDATE;"
psql -c "ALTER EXTENSION postgis UPDATE;"

5.3. Cleanup

# Xóa cluster cũ (CHỈ SAU KHI VERIFY HOÀN TẤT!)
# pg_upgrade tạo script delete_old_cluster.sh
./delete_old_cluster.sh

# Hoặc thủ công
rm -rf /var/lib/postgresql/17/main

# Disable PostgreSQL 17 service
sudo systemctl disable postgresql@17-main

5.4. Performance Validation

-- Kiểm tra query plan của các query quan trọng
EXPLAIN (ANALYZE, BUFFERS, VERBOSE) 
SELECT * FROM your_critical_table WHERE your_condition;

-- So sánh với baseline trước upgrade
-- Lưu ý: PostgreSQL 18 EXPLAIN ANALYZE tự động include BUFFERS

-- Kiểm tra AIO settings (PostgreSQL 18)
SHOW io_method;
-- Output: io_uring (Linux) hoặc worker

-- Monitor với pg_stat_io (mới trong PG16, cải thiện PG18)
SELECT * FROM pg_stat_io WHERE reads > 0;

6. Upgrade Với Logical Replication (Zero-Downtime)

6.1. Tổng Quan Architecture

┌──────────────────────┐         ┌──────────────────────┐
│   PostgreSQL 17.6    │         │   PostgreSQL 18.1    │
│     (Publisher)      │  ─────► │    (Subscriber)      │
│   Primary/Source     │   WAL   │   Target/Replica     │
│     Port: 5432       │         │     Port: 5433       │
└──────────────────────┘         └──────────────────────┘
                                          │
                                          ▼
                                   Cutover (DNS/HAProxy)

6.2. Setup Publisher (PostgreSQL 17)

-- 1. Enable logical replication trong postgresql.conf
-- wal_level = logical
-- max_replication_slots = 10
-- max_wal_senders = 10

-- 2. Tạo publication
CREATE PUBLICATION pg18_migration FOR ALL TABLES;

-- 3. Tạo replication user
CREATE USER repl_user WITH REPLICATION PASSWORD 'secure_password';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO repl_user;

-- 4. Cập nhật pg_hba.conf
-- host    all    repl_user    subscriber_ip/32    scram-sha-256
-- host    replication    repl_user    subscriber_ip/32    scram-sha-256

6.3. Setup Subscriber (PostgreSQL 18)

# 1. Dump schema only (không data)
pg_dump -h old_server -U postgres -s -d your_db > schema.sql

# 2. Restore schema vào PostgreSQL 18
psql -d your_db -f schema.sql
-- 3. Tạo subscription
CREATE SUBSCRIPTION pg18_sub
CONNECTION 'host=old_server port=5432 dbname=your_db user=repl_user password=secure_password'
PUBLICATION pg18_migration;

-- 4. Monitor sync progress
SELECT * FROM pg_stat_subscription;
SELECT * FROM pg_subscription_rel;

-- 5. Kiểm tra lag
SELECT 
  slot_name,
  pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) as lag
FROM pg_replication_slots;

6.4. Cutover Process

#!/bin/bash
# cutover.sh - Zero-downtime cutover script

echo "=== Starting Cutover Process ==="

# 1. Stop writes to old server (application level)
echo "1. Stopping application writes..."
# kubectl scale deployment/app --replicas=0
# hoặc update HAProxy/PgBouncer

# 2. Wait for replication to catch up
echo "2. Waiting for replication lag to be 0..."
while true; do
  LAG=$(psql -h new_server -p 5433 -t -c \
    "SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn) FROM pg_replication_slots WHERE slot_name='pg18_sub'")
  if [ "$LAG" -eq 0 ]; then
    break
  fi
  sleep 1
done

# 3. Disable subscription
echo "3. Disabling subscription..."
psql -h new_server -p 5433 -c "ALTER SUBSCRIPTION pg18_sub DISABLE;"
psql -h new_server -p 5433 -c "DROP SUBSCRIPTION pg18_sub;"

# 4. Reset sequences (quan trọng!)
echo "4. Resetting sequences..."
psql -h new_server -p 5433 -c "SELECT setval(c.oid, s.last_value) FROM pg_class c JOIN pg_sequences s ON c.relname = s.sequencename WHERE s.last_value IS NOT NULL;"

# 5. Switch traffic to new server
echo "5. Switching traffic..."
# Update DNS, HAProxy, PgBouncer, etc.

echo "=== Cutover Complete ==="

7. Rollback Plan

# QUAN TRỌNG: Với --link mode, bạn KHÔNG THỂ rollback sau khi start cluster mới!
# Do đó, LUÔN test kỹ trước khi start

# Nếu chưa start PostgreSQL 18:
# Chỉ cần start lại PostgreSQL 17
sudo systemctl start postgresql@17-main

7.2. Rollback Từ pg_upgrade --copy

# Nếu dùng --copy mode, cluster cũ vẫn còn nguyên
# Chỉ cần switch về cluster cũ

sudo systemctl stop postgresql@18-main
sudo systemctl start postgresql@17-main

7.3. Rollback Từ Logical Replication

-- Trên subscriber (PostgreSQL 18)
ALTER SUBSCRIPTION pg18_sub DISABLE;
DROP SUBSCRIPTION pg18_sub;

-- Switch traffic về PostgreSQL 17
-- Update DNS/HAProxy/PgBouncer

8. Monitoring & Troubleshooting

8.1. Monitoring Script

#!/bin/bash
# monitor-upgrade.sh

echo "=== PostgreSQL 18 Health Check ==="

# 1. Version
psql -c "SELECT version();"

# 2. Uptime
psql -c "SELECT pg_postmaster_start_time(), now() - pg_postmaster_start_time() as uptime;"

# 3. Active connections
psql -c "SELECT count(*) as active_connections FROM pg_stat_activity WHERE state = 'active';"

# 4. Database sizes
psql -c "SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY pg_database_size(datname) DESC;"

# 5. Long running queries
psql -c "SELECT pid, now() - pg_stat_activity.query_start AS duration, query FROM pg_stat_activity WHERE state = 'active' AND now() - pg_stat_activity.query_start > interval '5 minutes';"

# 6. Replication status (if applicable)
psql -c "SELECT * FROM pg_stat_replication;"

# 7. I/O statistics (PostgreSQL 18)
psql -c "SELECT backend_type, reads, writes, extends FROM pg_stat_io WHERE reads > 0 OR writes > 0;"

9. Best Practices Summary

9.1. Pre-Upgrade

  • [ ] Đọc kỹ Release Notes PostgreSQL 18
  • [ ] Test upgrade trên staging/development environment
  • [ ] Backup đầy đủ (logical + physical)
  • [ ] Document rollback plan
  • [ ] Notify stakeholders về maintenance window
  • [ ] Kiểm tra compatibility của tất cả extensions

9.2. During Upgrade

  • [ ] Sử dụng pg_upgrade --check trước khi upgrade thực sự
  • [ ] Monitor disk space trong quá trình upgrade
  • [ ] Giữ terminal session với screen hoặc tmux
  • [ ] Log tất cả output để troubleshoot

9.3. Post-Upgrade

  • [ ] Verify data integrity
  • [ ] Test critical queries và so sánh với baseline
  • [ ] Update extensions
  • [ ] Monitor performance 24-48 giờ
  • [ ] Cleanup old cluster sau khi verify hoàn tất
  • [ ] Update documentation và runbooks

10. Kết Luận

PostgreSQL 18 mang đến nhiều cải tiến đáng giá, đặc biệt là Asynchronous I/OStatistics Preservation giúp upgrade mượt mà hơn bao giờ hết. Với việc lập kế hoạch kỹ càng và follow đúng quy trình, bạn có thể upgrade production database với downtime tối thiểurisk thấp.

Tài liệu tham khảo:


Nếu bài viết hữu ích, hãy chia sẻ và để lại comment bên dưới! 👇

#postgresql #PostgreSQL 18 #Database #Database Migration #pg_upgrade