You run DELETE. The row is gone from your query. It is still on disk.
Postgres never overwrites a row in place. An update writes a new version and marks the old one dead. A delete just marks it dead. Both versions sit in the same table, and which one you see depends on when you started looking.
That is MVCC — multi-version concurrency control — and it buys something large. A long-running report reads a consistent snapshot while writes carry on around it. Readers never block writers. Writers never block readers.
The bill arrives as bloat. A million rows updated ten times each leaves a table holding some ten million row versions, where ninety percent of every page you read is dead weight. The indexes grow along with it.
VACUUM is the cleanup crew, marking dead space reusable. It runs automatically, and it falls behind exactly where you would least like it to — on the busiest tables, which are the ones producing the most corpses.
Nothing is free. Some costs just arrive on a different day than the decision.
