0

I recently ended up deleting most of the data in one of our tables, on purpose. Normally the table is append-mostly, with a short window during which updates might happen after the original insert. However, most of the data recently got orphaned, and we decided to delete it.

our_db=> delete from my_schema.table where a_column not in (select distinct value from my_schema.another_table);
DELETE 6418263

I expected VACUUM to take care of the now-mostly-empty pages, but it failed to do so!

our_db=> vacuum(analyze, verbose, disable_page_skipping) my_schema.table;
INFO:  vacuuming "my_schema.table"
INFO:  index "table_pkey" now contains 2 row versions in 46486 pages
DETAIL:  0 index row versions were removed.
46481 index pages have been deleted, 46481 are currently reusable.
CPU: user: 0.21 s, system: 0.00 s, elapsed: 2.76 s.
INFO:  index "table_idx1" now contains 2 row versions in 98919 pages
DETAIL:  0 index row versions were removed.
98914 index pages have been deleted, 98914 are currently reusable.
CPU: user: 0.47 s, system: 0.00 s, elapsed: 5.50 s.
INFO:  index "table_idx2" now contains 2 row versions in 22324 pages
DETAIL:  0 index row versions were removed.
22317 index pages have been deleted, 22317 are currently reusable.
CPU: user: 0.10 s, system: 0.00 s, elapsed: 1.63 s.
INFO:  index "table_idx3" now contains 2 row versions in 22252 pages
DETAIL:  0 index row versions were removed.
22245 index pages have been deleted, 22245 are currently reusable.
CPU: user: 0.11 s, system: 0.00 s, elapsed: 1.68 s.
INFO:  index "table_idx4" now contains 2 row versions in 22373 pages
DETAIL:  0 index row versions were removed.
22366 index pages have been deleted, 22366 are currently reusable.
CPU: user: 0.09 s, system: 0.01 s, elapsed: 1.39 s.
INFO:  index "table_idx5" now contains 2 row versions in 69761 pages
DETAIL:  0 index row versions were removed.
69755 index pages have been deleted, 69755 are currently reusable.
CPU: user: 0.32 s, system: 0.00 s, elapsed: 4.98 s.
INFO:  index "table_idx6" now contains 2 row versions in 71399 pages
DETAIL:  0 index row versions were removed.
71388 index pages have been deleted, 71388 are currently reusable.
CPU: user: 0.31 s, system: 0.02 s, elapsed: 5.55 s.
INFO:  "table": found 0 removable, 2 nonremovable row versions in 754123 out of 754123 pages
DETAIL:  0 dead row versions cannot be removed yet, oldest xmin: 6840941
There were 6439255 unused item pointers.
Skipped 0 pages due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 4.55 s, system: 0.20 s, elapsed: 53.89 s.
INFO:  vacuuming "pg_toast.pg_toast_16458"
INFO:  index "pg_toast_16458_index" now contains 0 row versions in 1 pages
DETAIL:  0 index row versions were removed.
0 index pages have been deleted, 0 are currently reusable.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
INFO:  "pg_toast_16458": found 0 removable, 0 nonremovable row versions in 0 out of 0 pages
DETAIL:  0 dead row versions cannot be removed yet, oldest xmin: 6840969
There were 0 unused item pointers.
Skipped 0 pages due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
INFO:  analyzing "my_schema.table"
INFO:  "table": scanned 30000 of 754123 pages, containing 0 live rows and 0 dead rows; 0 rows in sample, 0 estimated total rows
VACUUM

Only two rows are alive, yet over 750000 pages are still occupied, and scanned if sequential scan is needed:

our_db=> SELECT count(id) FROM my_schema.table;
 count 
-------
     2
(1 row)

our_db=> explain analyze SELECT count(content) FROM my_schema.table;
                                                        QUERY PLAN                                                         
---------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=754123.00..754123.01 rows=1 width=8) (actual time=26795.676..26795.676 rows=1 loops=1)
   ->  Seq Scan on table  (cost=0.00..754123.00 rows=1 width=1036) (actual time=25683.699..26795.666 rows=2 loops=1)
 Planning time: 0.052 ms
 Execution time: 26795.703 ms
(4 rows)

Why doesn't VACUUM get rid of empty frozen pages, when disable_page_skipping is enabled?

0

This has nothing to do with freezing.

VACUUM doesn't shrink the table, except that it will truncate any empty pages at the end of the table. Obviously an autovacuum job was faster than your manual VACUUM, because there were no dead tuples left.

To shrink and reorganize the table, run

VACUUM (FULL) my_schema.table;

but be aware that this will exclusively lock the table for the duration of the operation.

| improve this answer | |
  • Ok, it appears that Postgres 12 documentation is clearer on the "at the end of the table" part. I was under the impression that empty pages would be freed page-by-page. – Tuure Laurinolli May 28 at 6:05
  • Then again, I'm not so worried about freeing up the space, but I was really surprised to find that the empty pages get scanned during sequential scan. I would have expected them to get skipped, but thinking about it further, I'm not sure what could have caused such skipping. Perhaps some kind of "empty" bit in the visibility map? – Tuure Laurinolli May 28 at 7:08
  • Such a feature is not implemented. It would of course be possible, but the question is if it is worth the effort. Some badly bloated tables would benefit, but it would increase the visibility map size, which would be a (slight) disadvantage for everybody. You are welcome to probose it on the pgsql-hackers list if you are interested. – Laurenz Albe May 28 at 7:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.