Autovacuum: แก้ตารางบวมใน PostgreSQL ด้วย VACUUM ที่ถูกวิธี

เข้าใจ Autovacuum ของ PostgreSQL หาตารางที่บวมจาก pg_stat_user_tables หาสาเหตุที่การเก็บกวาดไม่ทัน ตั้งค่ารายตาราง และรู้ว่าเมื่อใดควรใช้ VACUUM FULL

โดย ทีมงาน THAI DATA CLOUD อัปเดต อ่าน 11 นาที

แชร์FacebookXLINELinkedIn
Autovacuum — Autovacuum: แก้ตารางบวมใน PostgreSQL ด้วย VACUUM ที่ถูกวิธี
Autovacuum: แก้ตารางบวมใน PostgreSQL ด้วย VACUUM ที่ถูกวิธี

คำตอบสั้น ๆ: เข้าใจ Autovacuum ของ PostgreSQL หาตารางที่บวมจาก pg_stat_user_tables หาสาเหตุที่การเก็บกวาดไม่ทัน ตั้งค่ารายตาราง และรู้ว่าเมื่อใดควรใช้ VACUUM FULL

คู่มือนี้อธิบาย Autovacuum ทีละขั้นตอน พร้อมคำสั่งที่ใช้ได้จริงและปัญหาที่พบบ่อย

Autovacuum คือกระบวนการเบื้องหลังของ PostgreSQL ที่คอยเก็บกวาดแถวข้อมูลที่ถูกลบหรือถูกแก้ไขแล้ว ทุกครั้งที่สั่ง UPDATE หรือ DELETE ระบบไม่ได้เขียนทับแถวเดิม แต่สร้างเวอร์ชันใหม่และทิ้งเวอร์ชันเก่าไว้เป็น dead tuple ตามหลัก MVCC ถ้าไม่มีใครเก็บกวาด ตารางจะบวม (bloat) ขนาดไฟล์โตขึ้นเรื่อย ๆ ทั้งที่จำนวนแถวไม่เพิ่ม และ Query ช้าลงเพราะต้องอ่านหน้าข้อมูลมากขึ้น

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

ลำดับการเกิด dead tuple จาก UPDATE และ DELETE จนถึงการเก็บกวาดโดย Autovacuum และการนำพื้นที่กลับมาใช้
ถ้ามี transaction ค้างนาน ขั้นที่สามจะลบแถวตายไม่ได้และตารางจะบวมต่อ

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

  • PostgreSQL 14 ขึ้นไปบน Linux และเข้า psql ด้วยผู้ใช้ postgres หรือเจ้าของตารางได้
  • สิทธิ์สร้าง extension ในฐานข้อมูล สำหรับใช้ pgstattuple วัดการบวม
  • พื้นที่ดิสก์ว่างอย่างน้อยเท่าขนาดตารางรวม Index ถ้าจะใช้ VACUUM FULL
  • ช่วงเวลาปิดปรับปรุงที่ตกลงกับผู้ใช้แล้ว สำหรับงานที่ต้องล็อกตาราง
  • ข้อมูลสำรองล่าสุด ดู คู่มือ pg_dump และ pg_restore

ขั้นตอนที่ 1: หาตารางที่มีแถวตายมากที่สุด

SELECT relname, n_live_tup, n_dead_tup,
       round(n_dead_tup * 100.0 / nullif(n_live_tup + n_dead_tup, 0), 1) AS dead_pct,
       last_vacuum, last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;

ตารางที่ dead_pct สูงกว่า 20% และเวลาเก็บกวาดล่าสุดเก่ามากคือผู้ต้องสงสัย สาเหตุที่พบบ่อยคือสูตรเริ่มทำงานของ Autovacuum ซึ่งรอให้แถวตายถึง autovacuum_vacuum_threshold (ค่าเริ่มต้น 50) บวก autovacuum_vacuum_scale_factor (ค่าเริ่มต้น 0.2) คูณจำนวนแถว ตารางสิบล้านแถวจึงต้องมีแถวตายราวสองล้านแถวก่อนจะถูกเก็บกวาดหนึ่งครั้ง

หน้าจอ psql แสดงตารางที่มีแถวตายมากที่สุดจาก pg_stat_user_tables และผล VACUUM VERBOSE ที่ลบแถวตายได้หมด
บรรทัด dead but not yet removable เป็น 0 แปลว่าไม่มีอะไรขวางการเก็บกวาด

ขั้นตอนที่ 2: วัดขนาดและการบวมจริง

SELECT pg_size_pretty(pg_total_relation_size('public.orders')) AS total,
       pg_size_pretty(pg_relation_size('public.orders')) AS table_only;

CREATE EXTENSION IF NOT EXISTS pgstattuple;
SELECT table_len, tuple_percent, dead_tuple_percent, free_percent
FROM pgstattuple('public.orders');

tuple_percent คือสัดส่วนข้อมูลจริง dead_tuple_percent คือแถวตายที่ยังไม่ถูกเก็บกวาด ส่วน free_percent คือที่ว่างภายในไฟล์ที่นำกลับมาใช้ได้ ถ้า free_percent สูงมาก เช่นเกิน 50% หลังลบข้อมูลจำนวนมาก แปลว่าไฟล์ใหญ่เกินข้อมูลจริงหลายเท่า pgstattuple อ่านทั้งตาราง จึงควรรันนอกเวลางาน หรือใช้ pgstattuple_approx ซึ่งเร็วกว่า

ขั้นตอนที่ 3: หาสิ่งที่ขวางการเก็บกวาด

VACUUM ลบได้เฉพาะแถวที่ไม่มี transaction ใดมองเห็นแล้ว transaction ที่เปิดค้างไว้นาน โดยเฉพาะสถานะ idle in transaction จะทำให้แถวตายทั้งฐานข้อมูลลบไม่ได้ ตรวจด้วยคำสั่งนี้

SELECT pid, usename, state, now() - xact_start AS duration, left(query, 60) AS query
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY xact_start
LIMIT 5;

SELECT slot_name, active, xmin, catalog_xmin FROM pg_replication_slots;

ถ้าเจอ transaction ที่ค้างเป็นชั่วโมง ให้ตรวจกับเจ้าของแอปก่อนปิดด้วย SELECT pg_terminate_backend(pid); และป้องกันในอนาคตด้วยการตั้งเวลาตัด session ที่ค้างกลาง transaction

ALTER SYSTEM SET idle_in_transaction_session_timeout = '10min';
SELECT pg_reload_conf();

Replication slot ที่ active เป็น false มานานก็ขวางได้เช่นกัน ถ้าเป็นเครื่องสำรองที่เลิกใช้แล้ว ให้ลบ slot นั้นออกด้วย pg_drop_replication_slot ดูเรื่อง slot เพิ่มเติมได้ใน คู่มือ Streaming Replication

ขั้นตอนที่ 4: รัน VACUUM ด้วยมือและดูความคืบหน้า

VACUUM (VERBOSE, ANALYZE) public.orders;

VACUUM ธรรมดาใช้ล็อกระดับ SHARE UPDATE EXCLUSIVE ผู้ใช้ยังอ่านและเขียนตารางได้ตามปกติ บล็อกเฉพาะคำสั่งแก้โครงสร้างตาราง จึงรันระหว่างวันได้ ถ้าตารางใหญ่ ให้เปิดอีกหน้าต่างเพื่อดูความคืบหน้า

SELECT pid, relid::regclass, phase, heap_blks_scanned, heap_blks_total
FROM pg_stat_progress_vacuum;

ในผลลัพธ์ VERBOSE ให้ดูบรรทัด tuples ถ้าค่า dead but not yet removable สูง แปลว่ายังมีสิ่งขวางตามขั้นตอนที่ 3 อยู่ การรันซ้ำจะไม่ช่วยจนกว่าจะแก้ต้นเหตุ

ขั้นตอนที่ 5: ตั้งค่า Autovacuum รายตาราง

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

ALTER TABLE public.orders SET (
  autovacuum_vacuum_scale_factor = 0.02,
  autovacuum_vacuum_threshold = 1000,
  autovacuum_analyze_scale_factor = 0.01
);

SELECT relname, reloptions FROM pg_class WHERE relname = 'orders';

ค่านี้ทำให้ตารางสิบล้านแถวถูกเก็บกวาดเมื่อมีแถวตายราวสองแสนแถว แทนที่จะรอถึงสองล้าน แต่ละรอบเบาลงและจบเร็วขึ้น ค่า analyze ที่ต่ำลงยังทำให้สถิติของ Planner สดอยู่เสมอ ถ้าการเก็บกวาดยังวิ่งไม่ทัน ให้ดูค่าความเร็ว เช่น autovacuum_vacuum_cost_limit ในคู่มือปรับแต่งเบื้องต้นก่อนเพิ่มจำนวน worker

ตารางเปรียบเทียบ VACUUM ธรรมดาที่ Autovacuum ใช้ กับ VACUUM FULL ทั้งการล็อก การคืนพื้นที่ และพื้นที่ดิสก์ที่ต้องเผื่อ
งานประจำใช้ VACUUM ธรรมดาเสมอ VACUUM FULL เป็นงานพิเศษที่ต้องวางแผน

ขั้นตอนที่ 6: คืนพื้นที่ดิสก์ด้วย VACUUM FULL เฉพาะเมื่อจำเป็น

คำเตือน: VACUUM FULL เขียนตารางใหม่ทั้งหมดและถือล็อก ACCESS EXCLUSIVE ตลอดเวลาที่ทำงาน ระหว่างนั้นไม่มีใครอ่านหรือเขียนตารางนี้ได้เลย และต้องมีพื้นที่ว่างเท่าขนาดตารางรวม Index ถ้าดิสก์เต็มกลางทางคำสั่งจะล้มเหลว ใช้เฉพาะหลังลบข้อมูลจำนวนมากครั้งเดียว และทำในช่วงปิดปรับปรุงเท่านั้น

SET lock_timeout = '5s';
VACUUM (FULL, VERBOSE, ANALYZE) public.orders;

lock_timeout ทำให้คำสั่งยกเลิกตัวเองถ้ารอล็อกนานเกิน 5 วินาที ป้องกันไม่ให้คำสั่งไปต่อคิวหลัง transaction ยาวแล้วบล็อกผู้ใช้ทุกคนที่ตามมา ถ้าปิดระบบไม่ได้เลย ส่วนขยาย pg_repack ซึ่งเป็นโครงการโอเพนซอร์สภายนอก ทำงานแบบเดียวกันโดยล็อกเพียงช่วงสั้น ๆ

ตรวจสอบว่าการเก็บกวาดกลับมาทำงานปกติ

รันคำสั่งในขั้นตอนที่ 1 ซ้ำในวันถัดไป ตารางที่ตั้งค่าแล้วควรมี last_autovacuum ใหม่กว่าเดิมและ dead_pct ต่ำกว่า 5 ถึง 10% อย่างสม่ำเสมอ และขนาดตารางจากขั้นตอนที่ 2 ไม่ควรโตเร็วกว่าจำนวนแถวที่เพิ่มขึ้นจริง

ปัญหาที่พบบ่อยเกี่ยวกับ Autovacuum และ VACUUM

VACUUM เสร็จแล้วแต่ขนาดไฟล์บนดิสก์ไม่ลดลง ผิดปกติไหม?

เป็นเรื่องปกติ VACUUM ธรรมดาทำเครื่องหมายว่าพื้นที่ของแถวตายนำกลับมาใช้ได้ แถวใหม่จะเขียนลงที่ว่างนั้นแทนการขยายไฟล์ ระบบคืนพื้นที่ให้ระบบปฏิบัติการได้เฉพาะหน้าว่างที่อยู่ท้ายไฟล์ ถ้าต้องการให้ไฟล์เล็กลงจริงต้องใช้ VACUUM FULL หรือ pg_repack ตามขั้นตอนที่ 6

Autovacuum ทำงานบ่อยแต่ n_dead_tup ไม่ลดลง เกิดจากอะไร?

แถวตายยังลบไม่ได้เพราะมีบางอย่างมองเห็นอยู่ ตรวจตามขั้นตอนที่ 3 ทั้ง transaction ที่ค้าง สถานะ idle in transaction และ replication slot ที่ไม่ใช้งาน รวมถึง prepared transaction ที่ค้างอยู่ใน pg_prepared_xacts เมื่อเอาสิ่งที่ขวางออก รอบถัดไปจะเก็บกวาดได้ทันที

ขึ้นคำเตือน database must be vacuumed within ... transactions ต้องทำอย่างไร?

นี่คือสัญญาณว่าหมายเลข transaction ใกล้วนรอบ (wraparound) ซึ่งร้ายแรง ถ้าปล่อยไว้ฐานข้อมูลจะหยุดรับคำสั่งเขียน ตรวจด้วย SELECT datname, age(datfrozenxid) FROM pg_database ORDER BY 2 DESC; แล้วเอาสิ่งที่ขวางในขั้นตอนที่ 3 ออก จากนั้นรัน VACUUM ธรรมดากับตารางที่อายุสูงที่สุด ไม่ต้องใช้ FULL และห้ามปิดการเก็บกวาดอัตโนมัติ

ปิด Autovacuum เพื่อลดโหลดของเครื่องได้ไหม?

ไม่ควร การปิดทำให้ตารางบวมต่อเนื่องและเสี่ยงปัญหา wraparound ถ้าการเก็บกวาดกินทรัพยากรมากเกินไป ให้ปรับความเร็วด้วยค่า cost หรือจัดตารางงานหนักไปช่วงกลางคืน การปิดรายตารางด้วย autovacuum_enabled = false เหมาะเฉพาะตารางพักข้อมูลชั่วคราวที่คุณรัน VACUUM เองหลังโหลดข้อมูลทุกครั้ง

รายการตรวจสุขภาพ Autovacuum ประจำเดือน ทั้งสัดส่วนแถวตาย transaction ค้าง replication slot และอายุ transaction ID
ใช้คำสั่งในคู่มือนี้ตรวจทุกข้อเดือนละครั้ง หรือหลังปล่อยฟีเจอร์ที่เขียนข้อมูลหนัก

ถ้าฐานข้อมูลของคุณโตเร็วผิดปกติหรือช้าลงเรื่อย ๆ และต้องการให้ช่วยวิเคราะห์ ติดต่อทีมงาน THAI DATA CLOUD หรือดูเครื่องสำหรับฐานข้อมูลที่ Cloud Server หลักการทั้งหมดอ่านต่อได้ที่ Routine Vacuuming ในเอกสาร PostgreSQL และ คำสั่ง VACUUM

อ่านเพิ่มเติมเกี่ยวกับ Autovacuum

แชร์FacebookXLINELinkedIn

มีคำถามเรื่องบริการ? ติดต่อทีมงาน