การวัดการพองตัวของตารางและดัชนีอย่างแม่นยำ
ใช้ pgstattuple และคำค้นสำหรับประมาณค่าเพื่อวัดพื้นที่ว่างก่อนเลือกแนวทางแก้ไข
การวัดการพองตัวของตารางและดัชนีอย่างแม่นยำ เป็นบทเรียน PostgreSQL Performance & Query Optimization ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน PostgreSQL Performance & Query Optimization และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส PostgreSQL Performance & Query Optimization มีบทเรียนทั้งหมด 4 บทเรียน
บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ
Why Bloat Happens
PostgreSQL uses MVCC (Multi-Version Concurrency Control). When you UPDATE or DELETE a row, the old version is not erased immediately. It becomes a dead tuple that still occupies space until VACUUM marks it reusable.
- Bloat = space occupied by dead tuples plus unfilled free space that the table or index no longer needs.
- Bloat inflates on-disk size, slows sequential scans, and reduces cache efficiency.
- Indexes bloat too: B-tree pages keep pointers to dead heap tuples until cleaned.
Before choosing a fix (VACUUM, VACUUM FULL, pg_repack, or REINDEX), you must first measure how much bloat actually exists. Guessing leads to unnecessary, disruptive maintenance.
Live vs Dead Tuples
The cheapest first signal comes from the statistics collector. pg_stat_user_tables tracks an estimate of live and dead tuples per table, updated by ANALYZE and autovacuum.
n_live_tup— estimated live rows.n_dead_tup— estimated dead rows awaiting cleanup.- A high
n_dead_tupratio suggests autovacuum is falling behind.
This is an estimate, not a byte-accurate measure, but it costs nothing and is a great triage filter.
SELECT relname,
n_live_tup,
n_dead_tup,
round(n_dead_tup * 100.0 / NULLIF(n_live_tup + n_dead_tup, 0), 2) AS dead_pct,
last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;Estimation vs Exact Measurement
There are two families of bloat measurement, each with trade-offs:
- Estimation queries read only catalog statistics (
pg_class,pg_statistic). They are fast and lock-free, but approximate — accuracy depends on fresh ANALYZE and column width assumptions. - pgstattuple physically scans the relation to count exact live/dead bytes. It is precise but I/O-heavy on large tables.
The practical workflow: use cheap estimation to find candidates, then use pgstattuple to confirm the worst ones before committing to remediation.
Installing pgstattuple
pgstattuple is a contrib extension shipped with PostgreSQL. It must be enabled per-database before use.
- Enabling it requires superuser or a role with
CREATEon the database. - Its functions require the
pg_stat_scan_tablesrole (or superuser) to run against arbitrary relations.
Once installed, you get pgstattuple(), pgstatindex(), and the lighter pgstattuple_approx().
CREATE EXTENSION IF NOT EXISTS pgstattuple;Reading pgstattuple Output
pgstattuple('relation') performs a full scan and returns one row of byte-level facts about the heap.
table_len— total relation size in bytes.tuple_count/tuple_len— count and total bytes of live tuples.dead_tuple_count/dead_tuple_len— dead tuples and their bytes.free_space/free_percent— reusable free space.
The key bloat signal is dead_tuple_percent plus free_percent: together they tell you how much of the file is not holding live data.
SELECT table_len,
tuple_count,
tuple_len,
dead_tuple_count,
dead_tuple_len,
dead_tuple_percent,
free_space,
free_percent
FROM pgstattuple('public.orders');The Cost of a Full Scan
pgstattuple() reads every page of the relation. On a 500 GB table that is a lot of I/O and can evict useful data from cache.
- It takes only an ACCESS SHARE lock, so it does not block reads or writes — but the I/O load is real.
- For large tables, prefer
pgstattuple_approx(), which uses the visibility map to skip all-visible pages and samples the rest. approxreturnsapprox_free_percentanddead_tuple_percentclose to the exact values at a fraction of the cost.
Rule of thumb: estimate first, run approx on mid-size tables, reserve exact pgstattuple() for the final confirmation on a specific suspect.
SELECT table_len,
approx_tuple_count,
approx_tuple_percent,
dead_tuple_count,
dead_tuple_percent,
approx_free_percent
FROM pgstattuple_approx('public.orders');Measuring Index Bloat
Indexes bloat independently of their table. Use pgstatindex() for B-tree indexes to get structural detail.
avg_leaf_density— percentage of leaf pages filled with useful data. Healthy indexes sit near 90%; values dropping toward 50% signal heavy bloat.leaf_fragmentation— how out-of-order leaf pages are; high fragmentation hurts range scans.index_sizeandinternal_pages/leaf_pagesdescribe the tree shape.
A low avg_leaf_density is the clearest argument for a REINDEX (ideally REINDEX ... CONCURRENTLY).
SELECT version,
index_size,
leaf_pages,
avg_leaf_density,
leaf_fragmentation
FROM pgstatindex('public.orders_customer_id_idx');The Estimation Query Approach
When you cannot afford a scan at all, the community bloat estimation query (from check_postgres / pgsql-bloat-estimation) computes expected size from statistics and compares it to actual size.
Its core idea:
- Take the average row width from
pg_statistic(theavg_widthper column ANALYZE recorded). - Add per-tuple header and alignment overhead, then divide table size by the expected tuples-per-page.
- The gap between expected pages and actual pages is the estimated bloat.
It is approximate and sensitive to stale stats, but runs in milliseconds across the whole database.
Why Estimates Drift
Estimation accuracy collapses when its inputs are wrong. Watch for these traps:
- Stale statistics: if ANALYZE has not run recently,
avg_widthand row counts are outdated. RunANALYZEbefore trusting estimates. - Wide variable-length columns: highly variable
text/jsonbwidths make per-row averages unreliable. - TOAST: large values stored out-of-line live in a separate TOAST table; heap estimates miss that storage entirely.
- Fillfactor: a table built with
fillfactor < 100intentionally leaves free space — that is not bloat.
Always cross-check a surprising estimate with pgstattuple_approx() before acting.
ANALYZE public.orders;Don't Forget the TOAST Table
Large column values are pushed to a hidden TOAST table that bloats on its own. A heap may look clean while its TOAST relation is enormous.
- Find the TOAST relation via
pg_class.reltoastrelid. - Run
pgstattuple()directly on the TOAST relation OID to measure its dead space.
Tables with frequently-updated jsonb or bytea columns often hide most of their bloat in TOAST.
SELECT c.relname,
pg_size_pretty(pg_relation_size(c.reltoastrelid)) AS toast_size,
t.dead_tuple_percent
FROM pg_class c
CROSS JOIN LATERAL pgstattuple(c.reltoastrelid) AS t
WHERE c.relname = 'orders'
AND c.reltoastrelid <> 0;From Numbers to a Decision
Once you have accurate figures, map them to a remediation path:
- dead_tuple_percent high, free_percent high: autovacuum is behind — a plain
VACUUM(or tuning autovacuum) usually reclaims reusable space without rewriting the file. - free_percent very high but table won't shrink: the file has trailing free space that VACUUM can't return to the OS — consider
pg_repack(online) orVACUUM FULL(locks the table). - Index avg_leaf_density low:
REINDEX CONCURRENTLY.
Set a threshold (e.g. act only above ~20% bloat and a meaningful absolute size) so you don't run disruptive maintenance for trivial gains.
Quick Check
You suspect a 400 GB table is badly bloated and want an accurate bloat figure with the least I/O impact, given autovacuum keeps the visibility map fairly up to date. Which tool fits best?
Recap
You now have a layered method to quantify bloat before acting:
- Triage with
pg_stat_user_tables(n_dead_tup ratio) — free and instant. - Estimate across the whole DB with statistics-based bloat queries — fast, but verify with fresh
ANALYZE. - Confirm precisely with
pgstattuple(), orpgstattuple_approx()on large tables, reading dead_tuple_percent and free_percent. - Indexes: use
pgstatindex()and watchavg_leaf_densityandleaf_fragmentation. - Don't forget TOAST, and discount intentional fillfactor free space.
Only after the numbers cross a meaningful threshold do you pick VACUUM, pg_repack, VACUUM FULL, or REINDEX — measurement drives the remediation, never the reverse.
เรียนรู้ SQL ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 22
- บทเรียน
- 88
คำถามที่พบบ่อย
บทเรียน “การวัดการพองตัวของตารางและดัชนีอย่างแม่นยำ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การวัดการพองตัวของตารางและดัชนีอย่างแม่นยำ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส PostgreSQL Performance & Query Optimization ให้อัปเกรดเป็น CoddyKit PRO คอร์ส PostgreSQL Performance & Query Optimization มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การวัดการพองตัวของตารางและดัชนีอย่างแม่นยำ”
ใช้ pgstattuple และคำค้นสำหรับประมาณค่าเพื่อวัดพื้นที่ว่างก่อนเลือกแนวทางแก้ไข คุณปฏิบัติ PostgreSQL Performance & Query Optimization ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน PostgreSQL Performance & Query Optimization หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน PostgreSQL Performance & Query Optimization บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “การวัดการพองตัวของตารางและดัชนีอย่างแม่นยำ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน PostgreSQL Performance & Query Optimization นี้ได้ไหม
ได้ บทเรียน PostgreSQL Performance & Query Optimization ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การวัดการพองตัวของตารางและดัชนีอย่างแม่นยำ
- การเรียกคืนพื้นที่ด้วย pg_repack
- การปรับ Fillfactor สำหรับตารางที่มีการปรับปรุงข้อมูลสูง
- โครงสร้างภายใน TOAST และการจัดเก็บค่าขนาดใหญ่