Skip to content
dbexplore

Question: Vacuum, bloat and wraparound

Why didn't deleting rows free any disk space?

Answered in the first paragraph. Last updated .

Because a delete does not take anything out of the file. It marks the row version as no longer current and leaves it exactly where it was, so the moment the statement commits the file is the size it always was. Later, vacuum makes that space available for the same table to write into again. Reusable and returned are two different outcomes, and only one of them shows up on a disk graph.

Why the row has to stay

Other transactions may still be entitled to see it. A statement that began before your delete has a view of the table in which that row exists, and the only way to serve both readers is to keep both versions until nobody needs the old one. That is how concurrency works here, and it is the same mechanism that makes an update leave a copy behind. The cost is that deletion is bookkeeping, not removal.

Once the old versions are beyond anyone’s view, vacuum records the space as free inside each page. The next insert or update on that table can use it. From outside the database nothing has changed: the file has the same number of pages and the operating system reports the same usage. A table in steady state, deleting as much as it inserts, is working correctly when its size holds flat.

There is one narrow case where the file does shrink by itself. If the pages at the very end of the file become entirely empty, vacuum can cut them off. That depends on where the deleted rows happened to live, so it fires on a table cleared from the tail and almost never on one deleted from the middle.

When the space genuinely has to go back

First be sure it does. If the table will write the same volume again next week, returning the space costs an outage and buys a week. Dead rows falling to zero after a vacuum tells you the cleanup worked, not that the file will shrink.

If it really must go, the rewrite command does it and holds the table shut for the duration, which does VACUUM FULL lock the table covers, and the online rewrite extensions do the same work with a lock measured in seconds.

Do not forget the indexes. They hold their own entries for every row version, and clearing a table leaves those pages behind in the same way, which is one of the reasons an index ends up bigger than its table. If the file keeps returning to the same size after every rewrite, the rewrite is the wrong tool: autovacuum and table bloat covers why space stops being reused.

Put every Postgres you run on autopilot.

We onboard teams in small batches. Tell us about your fleet and we will reach out when a seat opens. One email, no drip campaign.