PostgreSQL Initial Configuration — ตั้งค่าเริ่มต้นหลังติดตั้ง

หลังจากติดตั้ง PostgreSQL เสร็จเรียบร้อยแล้ว ขั้นตอนถัดไปที่สำคัญไม่แพ้กันคือการตั้งค่าเริ่มต้นให้เหมาะกับการใช้งานจริง ค่า Default ที่มาพร้อมกับการติดตั้งนั้นถูกออกแบบมาให้ทำงานได้บนทุกเครื่อง แต่ไม่ได้ปรับให้เหมาะกับสภาพแวดล้อมเฉพาะของเซิร์ฟเวอร์แต่ละเครื่อง

บทความนี้จะแนะนำการตั้งค่าเริ่มต้นที่ควรทำทันทีหลังติดตั้ง ครอบคลุมตั้งแต่การจัดการหน่วยความจำ, WAL, Checkpoint, Connection Limits, Locale ไปจนถึงการตั้งค่า Timezone และ Statement Timeout เพื่อให้ระบบฐานข้อมูลพร้อมสำหรับ Production ตั้งแต่วันแรก

ตำแหน่งไฟล์ตั้งค่าหลัก

ก่อนเริ่มปรับแต่ง ต้องรู้ก่อนว่าไฟล์ตั้งค่าอยู่ที่ไหน ตำแหน่งจะแตกต่างกันตามวิธีการติดตั้งและระบบปฏิบัติการ

ระบบปฏิบัติการตำแหน่ง postgresql.conf
Ubuntu/Debian (APT)/etc/postgresql/16/main/postgresql.conf
CentOS/AlmaLinux (YUM)/var/lib/pgsql/16/data/postgresql.conf

สามารถตรวจสอบตำแหน่งได้จากภายในตัวฐานข้อมูลเอง

sudo -i -u postgres psql -c "SHOW config_file;"
sudo -i -u postgres psql -c "SHOW data_directory;"

หลังแก้ไขไฟล์ตั้งค่า บางพารามิเตอร์สามารถโหลดใหม่ได้โดยไม่ต้องรีสตาร์ท ขณะที่บางตัวต้องรีสตาร์ทบริการเท่านั้น

# โหลดค่าใหม่แบบไม่ต้องรีสตาร์ท (สำหรับพารามิเตอร์ที่รองรับ)
sudo -i -u postgres psql -c "SELECT pg_reload_conf();"

# รีสตาร์ทบริการ (สำหรับพารามิเตอร์ที่ต้องการ restart)
sudo systemctl restart postgresql       # Ubuntu/Debian
sudo systemctl restart postgresql-16    # CentOS/AlmaLinux

ตรวจสอบว่าพารามิเตอร์ใดต้อง restart และพารามิเตอร์ใดแค่ reload ได้ด้วยคำสั่ง

SELECT name, context FROM pg_settings
WHERE name IN ('shared_buffers', 'work_mem', 'max_connections', 'log_min_duration_statement');

ค่า context ที่เป็น postmaster หมายถึงต้อง restart ส่วน sighup หมายถึง reload ก็พอ และ user หมายถึงเปลี่ยนได้ระดับ session

ปรับแต่งหน่วยความจำ (Memory Tuning)

การจัดสรรหน่วยความจำที่เหมาะสมเป็นปัจจัยหลักที่ส่งผลต่อประสิทธิภาพ พารามิเตอร์สำคัญมี 4 ตัว

shared_buffers

พื้นที่หน่วยความจำที่ใช้เก็บข้อมูลที่อ่านจากดิสก์ (Cache) ค่าเริ่มต้นคือ 128 MB ซึ่งน้อยเกินไปสำหรับเซิร์ฟเวอร์ Production แนะนำให้ตั้งค่าเป็น 25% ของ RAM ทั้งหมด

# สำหรับเซิร์ฟเวอร์ RAM 4 GB
shared_buffers = '1GB'

# สำหรับเซิร์ฟเวอร์ RAM 8 GB
shared_buffers = '2GB'

# สำหรับเซิร์ฟเวอร์ RAM 16 GB
shared_buffers = '4GB'

ข้อควรระวัง: ไม่ควรตั้งเกิน 40% ของ RAM เพราะระบบปฏิบัติการก็ต้องใช้หน่วยความจำสำหรับ File System Cache เช่นกัน

effective_cache_size

ค่านี้ไม่ได้จองหน่วยความจำจริง แต่เป็นตัวบอก Query Planner ว่าระบบมี Cache ทั้งหมดเท่าไหร่ (รวม OS Cache) เพื่อให้ Planner ตัดสินใจเลือกแผนการ Query ได้ดีขึ้น แนะนำตั้งไว้ที่ 50-75% ของ RAM

# สำหรับเซิร์ฟเวอร์ RAM 4 GB
effective_cache_size = '3GB'

# สำหรับเซิร์ฟเวอร์ RAM 8 GB
effective_cache_size = '6GB'

work_mem

หน่วยความจำที่แต่ละ Operation ภายใน Query สามารถใช้ได้ เช่น Sort, Hash Join, Hash Aggregate ค่านี้จะถูกจองต่อ Operation ไม่ใช่ต่อ Query ดังนั้น Query เดียวอาจใช้ work_mem หลายเท่า

# สูตรคำนวณเบื้องต้น
# work_mem = RAM / max_connections / 4

# ตัวอย่าง: 4 GB RAM, 100 connections
work_mem = '10MB'

# ตัวอย่าง: 8 GB RAM, 100 connections
work_mem = '20MB'

ถ้าตั้งค่าสูงเกินไป เมื่อมีผู้ใช้จำนวนมากพร้อมกัน อาจทำให้ RAM ไม่พอ ถ้าตั้งต่ำเกินไป ระบบจะใช้ Temp Files บนดิสก์ซึ่งช้ากว่ามาก

maintenance_work_mem

หน่วยความจำสำหรับงานบำรุงรักษา เช่น VACUUM, CREATE INDEX, ALTER TABLE ADD FOREIGN KEY สามารถตั้งสูงกว่า work_mem ได้เพราะงานเหล่านี้ทำทีละอย่าง

# แนะนำ: RAM / 16 หรือไม่เกิน 1 GB
maintenance_work_mem = '256MB'    # สำหรับ 4 GB RAM
maintenance_work_mem = '512MB'    # สำหรับ 8 GB RAM

ตั้งค่า WAL และ Checkpoint

WAL (Write-Ahead Logging) เป็นกลไกที่รับประกันว่าข้อมูลจะไม่สูญหายแม้เซิร์ฟเวอร์จะหยุดทำงานกะทันหัน ทุกการเปลี่ยนแปลงจะถูกเขียนลง WAL ก่อนเขียนลง Data File จริง

# ขนาด Buffer สำหรับเขียน WAL
wal_buffers = '16MB'

# ขนาด WAL สูงสุดก่อน Checkpoint
max_wal_size = '2GB'
min_wal_size = '512MB'

# ความถี่ Checkpoint
checkpoint_timeout = '10min'
checkpoint_completion_target = 0.9

อธิบายแต่ละพารามิเตอร์

  • wal_buffers — Buffer ชั่วคราวก่อนเขียนลง WAL File ค่า 16 MB เพียงพอสำหรับเซิร์ฟเวอร์ส่วนใหญ่
  • max_wal_size — ขนาด WAL สะสมสูงสุดก่อนบังคับ Checkpoint ค่าที่สูงขึ้นลดความถี่ Checkpoint แต่ใช้พื้นที่ดิสก์มากขึ้น
  • checkpoint_timeout — ระยะเวลาสูงสุดระหว่าง Checkpoint แต่ละครั้ง ค่าเริ่มต้น 5 นาที แนะนำเพิ่มเป็น 10-15 นาทีสำหรับ Production
  • checkpoint_completion_target — กระจายการเขียนข้อมูลออกไปในช่วง Checkpoint เพื่อลด I/O spike ค่า 0.9 หมายถึงใช้ 90% ของเวลาก่อน Checkpoint ถัดไปในการเขียน

กำหนด Connection Limits

จำนวน Connection ที่เปิดพร้อมกันส่งผลโดยตรงต่อการใช้ทรัพยากร แต่ละ Connection ใช้หน่วยความจำประมาณ 5-10 MB การตั้ง max_connections สูงเกินไปโดยไม่จำเป็นจะสิ้นเปลืองทรัพยากร

# จำนวน Connection สูงสุด
max_connections = 100

# สำรองไว้สำหรับ Superuser (เข้าซ่อมแซมเมื่อ Connection เต็ม)
superuser_reserved_connections = 3

สำหรับแอปพลิเคชันที่ต้องการ Connection จำนวนมาก แนะนำให้ใช้ Connection Pooler เช่น PgBouncer แทนการเพิ่ม max_connections เพราะ Pooler จะจัดการ Connection อย่างมีประสิทธิภาพกว่า

ตั้งค่า Locale และ Encoding

Locale และ Encoding ส่งผลต่อการจัดเรียง (Collation), การเปรียบเทียบตัวอักษร และการจัดเก็บข้อมูลภาษาต่าง ๆ ค่าเหล่านี้ตั้งได้ตอนสร้าง Cluster หรือตอนสร้างฐานข้อมูลใหม่

# ตรวจสอบ Locale ปัจจุบัน
sudo -i -u postgres psql -c "SHOW lc_collate;"
sudo -i -u postgres psql -c "SHOW lc_ctype;"
sudo -i -u postgres psql -c "SHOW server_encoding;"

สำหรับงานที่รองรับหลายภาษารวมถึงภาษาไทย แนะนำใช้ UTF-8 เป็น Encoding หลัก

# สร้างฐานข้อมูลใหม่ด้วย Encoding ที่เหมาะสม
CREATE DATABASE myapp
  ENCODING 'UTF8'
  LC_COLLATE 'en_US.UTF-8'
  LC_CTYPE 'en_US.UTF-8'
  TEMPLATE template0;

ถ้าต้องการเปลี่ยน Locale ของ Cluster ที่มีอยู่แล้ว ต้อง dump ข้อมูล สร้าง Cluster ใหม่ด้วย Locale ที่ต้องการ แล้ว restore กลับ ไม่สามารถเปลี่ยนได้โดยตรง

กำหนด Timezone

Timezone เป็นสิ่งที่มักถูกมองข้าม แต่ส่งผลต่อคอลัมน์ประเภท timestamptz และฟังก์ชัน now() โดยตรง

# ตรวจสอบ Timezone ปัจจุบัน
SHOW timezone;

# ตั้งค่าใน postgresql.conf
timezone = 'Asia/Bangkok'

# หรือตั้งค่าระดับฐานข้อมูล
ALTER DATABASE myapp SET timezone TO 'Asia/Bangkok';

# ตั้งค่าระดับผู้ใช้
ALTER USER appuser SET timezone TO 'Asia/Bangkok';

แนะนำให้ตั้ง Timezone ของเซิร์ฟเวอร์เป็น UTC แล้วจัดการแปลง Timezone ที่ชั้น Application แทน วิธีนี้ช่วยหลีกเลี่ยงปัญหาเมื่อผู้ใช้อยู่คนละ Timezone และทำให้การ Debug ง่ายขึ้น อย่างไรก็ตาม สำหรับระบบที่ใช้งานเฉพาะในประเทศไทย การตั้งเป็น Asia/Bangkok ก็เป็นทางเลือกที่สมเหตุสมผล

ตั้งค่า Statement Timeout

Statement Timeout เป็นเกราะป้องกันไม่ให้ Query ที่ทำงานนานเกินไปกิน CPU และ Lock ทรัพยากรของระบบ ค่าเริ่มต้นคือ 0 (ไม่จำกัด) ซึ่งอันตรายสำหรับ Production

# ตั้งค่าระดับเซิร์ฟเวอร์ (postgresql.conf)
statement_timeout = '30s'        # ยกเลิก Query ที่ทำงานเกิน 30 วินาที

# ตั้งค่าระดับฐานข้อมูล
ALTER DATABASE myapp SET statement_timeout TO '60s';

# ตั้งค่าระดับผู้ใช้ (สำหรับ batch job ที่ต้องทำงานนาน)
ALTER USER batch_user SET statement_timeout TO '300s';

# ตั้งค่าระดับ Session (ชั่วคราว)
SET statement_timeout TO '120s';

นอกจาก Statement Timeout ยังมี Timeout อื่น ๆ ที่ควรรู้จัก

# Timeout สำหรับรอ Lock
lock_timeout = '10s'

# Timeout สำหรับ Transaction ที่เปิดค้างไว้
idle_in_transaction_session_timeout = '60s'

idle_in_transaction_session_timeout สำคัญมากเพราะ Transaction ที่เปิดค้างไว้จะกัน VACUUM ไม่ให้ทำความสะอาดข้อมูล ทำให้ตาราง Bloat ได้

ปรับแต่งสำหรับ SSD

ถ้าเซิร์ฟเวอร์ใช้ SSD (ซึ่ง Cloud VPS ส่วนใหญ่ใช้) ควรปรับพารามิเตอร์เหล่านี้เพื่อให้ Query Planner เลือกแผนที่เหมาะสมกับความเร็วของดิสก์

# ลด random_page_cost ลง (ค่าเริ่มต้น 4.0 สำหรับ HDD)
random_page_cost = 1.1

# เพิ่มจำนวน I/O ที่ทำพร้อมกันได้
effective_io_concurrency = 200

random_page_cost บอก Planner ว่าการอ่านแบบ Random มีต้นทุนเท่าไหร่เมื่อเทียบกับ Sequential ค่าที่ต่ำลงทำให้ Planner มีแนวโน้มเลือก Index Scan มากขึ้น ซึ่งเหมาะกับ SSD ที่ Random Read เร็วเกือบเท่า Sequential Read

ตั้งค่า Default Statistics Target

Statistics Target กำหนดความละเอียดของข้อมูลสถิติที่ ANALYZE เก็บ ยิ่งค่าสูง Planner ก็ยิ่งมีข้อมูลมากในการตัดสินใจ แต่ ANALYZE จะใช้เวลานานขึ้น

# ค่าเริ่มต้นคือ 100 (เพียงพอสำหรับส่วนใหญ่)
default_statistics_target = 100

# เพิ่มเป็น 200-500 สำหรับตารางที่มีข้อมูลกระจายไม่สม่ำเสมอ
ALTER TABLE orders ALTER COLUMN status SET STATISTICS 500;

ตั้งค่า Huge Pages (Linux)

สำหรับเซิร์ฟเวอร์ที่มี shared_buffers ตั้งค่าสูง (1 GB ขึ้นไป) การเปิด Huge Pages ช่วยลดภาระของ CPU ในการจัดการ Page Table ของระบบปฏิบัติการ

# ขั้นตอน 1: ตรวจสอบขนาด Huge Page
grep Hugepagesize /proc/meminfo
# ปกติจะได้ 2048 kB (2 MB)

# ขั้นตอน 2: คำนวณจำนวน Huge Pages ที่ต้องการ
# ถ้า shared_buffers = 1GB → ต้องการ ~520 pages (1GB / 2MB + buffer)
sudo sysctl -w vm.nr_hugepages=550

# ทำให้ถาวร
echo "vm.nr_hugepages = 550" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# ขั้นตอน 3: เปิดใน postgresql.conf
huge_pages = try    # ใช้ถ้ามี ถ้าไม่มีก็ทำงานปกติ

ค่า try เป็นตัวเลือกที่ปลอดภัย เพราะถ้าระบบไม่รองรับหรือจัดสรร Huge Pages ไม่พอ ก็จะใช้ Regular Pages แทนโดยไม่เกิดข้อผิดพลาด

ตัวอย่างไฟล์ตั้งค่าแบบสมบูรณ์

ด้านล่างเป็นตัวอย่างการตั้งค่าสำหรับเซิร์ฟเวอร์ RAM 4 GB ที่ใช้ SSD รองรับ 100 Connection

# === Memory ===
shared_buffers = '1GB'
effective_cache_size = '3GB'
work_mem = '10MB'
maintenance_work_mem = '256MB'
huge_pages = try

# === WAL & Checkpoint ===
wal_buffers = '16MB'
max_wal_size = '2GB'
min_wal_size = '512MB'
checkpoint_timeout = '10min'
checkpoint_completion_target = 0.9

# === Connection ===
max_connections = 100
superuser_reserved_connections = 3

# === SSD Optimization ===
random_page_cost = 1.1
effective_io_concurrency = 200

# === Timezone ===
timezone = 'Asia/Bangkok'

# === Timeout ===
statement_timeout = '30s'
lock_timeout = '10s'
idle_in_transaction_session_timeout = '60s'

# === Statistics ===
default_statistics_target = 100

# === Logging (ดูรายละเอียดในบทความ Logging) ===
logging_collector = on
log_min_duration_statement = 1000
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on

# === Autovacuum ===
autovacuum = on
autovacuum_max_workers = 3
autovacuum_vacuum_scale_factor = 0.1
autovacuum_analyze_scale_factor = 0.05

ตรวจสอบค่าที่ตั้งไว้

หลังแก้ไข postgresql.conf และรีสตาร์ทบริการแล้ว ตรวจสอบว่าค่าที่ตั้งไว้มีผลจริงด้วยคำสั่งต่อไปนี้

sudo -i -u postgres psql -c "
SELECT name, setting, unit, source, short_desc
FROM pg_settings
WHERE source != 'default'
  AND name NOT LIKE 'lc_%'
ORDER BY name;"

คำสั่งนี้จะแสดงเฉพาะพารามิเตอร์ที่ถูกเปลี่ยนจากค่าเริ่มต้น พร้อมระบุแหล่งที่มา (configuration file, session, database เป็นต้น) ช่วยให้ตรวจสอบได้ง่ายว่าค่าทั้งหมดถูกต้อง

เครื่องมือช่วยคำนวณค่าตั้งต้น

ถ้าไม่แน่ใจว่าควรตั้งค่าเท่าไหร่ สามารถใช้เครื่องมือออนไลน์ที่ช่วยคำนวณค่าที่เหมาะสมตามสเปกเซิร์ฟเวอร์ เครื่องมือยอดนิยมคือ PGTune (pgtune.leopard.in.ua) เพียงใส่จำนวน RAM, CPU, ประเภทดิสก์ และลักษณะการใช้งาน (Web Application, OLTP, Data Warehouse) ก็จะได้ค่าตั้งค่าที่แนะนำ

อย่างไรก็ตาม ค่าจาก PGTune เป็นจุดเริ่มต้นที่ดี แต่ควรปรับเพิ่มเติมตาม Workload จริงของระบบ โดยดูจาก pg_stat_statements, log ของ Query ที่ช้า และตัวเลข Cache Hit Ratio

สรุป

การตั้งค่าเริ่มต้นหลังติดตั้งเป็นขั้นตอนที่ไม่ควรข้าม พารามิเตอร์สำคัญที่ต้องปรับได้แก่ shared_buffers, effective_cache_size, work_mem สำหรับหน่วยความจำ ค่า WAL และ Checkpoint สำหรับความทนทานของข้อมูล max_connections สำหรับจำกัดทรัพยากร Timezone สำหรับข้อมูลเวลาที่ถูกต้อง และ Statement Timeout สำหรับป้องกัน Query ที่ทำงานนานเกินไป เมื่อตั้งค่าเหล่านี้เรียบร้อยแล้ว ระบบฐานข้อมูลจะพร้อมรองรับงานจริงได้อย่างมีประสิทธิภาพ

แนะนำบริการ DE

หากคุณกำลังมองหาเซิร์ฟเวอร์สำหรับรัน PostgreSQL ที่ปรับแต่งได้อย่างเต็มที่ Cloud VPS ของ DE ตอบโจทย์ได้ดี เพราะมี SSD Storage และให้สิทธิ์ root สำหรับแก้ไข postgresql.conf ตามคำแนะนำในบทความนี้โดยตรง

สำหรับผู้ที่ต้องการใช้งานฐานข้อมูลควบคู่กับเว็บไซต์โดยไม่ต้องตั้งค่าเซิร์ฟเวอร์เอง Cloud Hosting ของ DE เป็นทางเลือกที่สะดวก มีการตั้งค่าให้พร้อมใช้งานตั้งแต่แรก