Improved vacuum in PostgreSQL 13
amitkapila16.blogspot.com
amitkapila16.blogspot.com
Of course this approach requires some maintenance of that separate place. But such maintenance doesn't compete for I/O with regular workload and doesn't hurt performance.
So after decades of progress and improvement it looks like this latter approach turned out to be more efficient and is winning historically.
It runs automatically in the background, with rather conservative settings that usually prevent it from having much perf impact.
The system also ties into MVCC because deleted rows in the main table cannot be removed while any query can still access some other version of that row. The same is true when changing any key column in an index. In those cases, the update is a delete and insert within that index. And the deleted row must stick around if there is any transaction that could read it.
So while SQL Server does not get as many rows that needs to be vacuumed as PostgreSQL does, just storing the versions separately certainly does not completely eliminate the need for after the fact cleanup.
It seems the important difference is whether the table needs to be scanned to find dead rows. In Postgres it seems the answer is yes. In MySQL it keeps this info as a separate scratchpad to quickly find the rows to remove once they are allowed to be.
Both approaches have advantages and disadvantages. For example, in MySQL, if you have a very long running transaction such that it can't purge rows for a long time, you can actually overflow the rollback segment onto disk and purging slows down dramatically. You might even need to pause writes to get it to recover, although I haven't ever seen it get that bad. With a table scan approach it degrades more gracefully in that scenario.
As you noted, this can get painful since purge effectively gets blocked -- if you have a high-volume OLTP workload with heavy emphasis on quick small UPDATEs / DELETEs, and then introduce a single very long-running read transaction (such as a logical dump/backup), purge can't make progress. But this is just due to the current implementation. In theory a smarter system could differentiate between row versions actually needed by old transactions vs ones that are inaccessible to all transactions.
Also, you need to copy entire row before updating it or deleting it, not sure how significant this cost is in practice.
Honestly, performance isn't as important as the capacity of getting an atomic snapshot of your data with any reliability.
https://www.postgresql.eu/events/pgconfeu2018/sessions/sessi...
https://github.com/EnterpriseDB/zheap/tree/master/src/backen...
https://www.postgresql.org/message-id/CAA4eK1%2BYtM5vxzSM2NZ...
https://rhaas.blogspot.com/2018/01/do-or-undo-there-is-no-va...
There are several successful databases that use in page MVCC storage. I'd suggest going through Andy Pavlo's slide decks for a summary: https://15721.courses.cs.cmu.edu/spring2020/, in particular slide 33 from lecture 3 has a summary. Also there are papers wheree his group directly benchmarks the various design alternatives within their test database, Peloton.
The TL;DR, is there's a lot of details to how this stuff works, many tradeoffs involved, and it's by no means whatsoever clear that Postgres has somehow made the wrong choice.
I immediately got some warnings in pgadmin that some tables need to be vacuumed. Is this really necessary? Can i just vacuum everything at night on a cronjob?
Their wiki is pretty amazing source for postgres's operation: https://www.postgresql.org/docs/12/routine-vacuuming.html
A common misconception about (auto) vacuum is, that you need to turn it off, to avoid performance problems.
The truth is: you need to make it more aggressive (= run it more frequently) if you have problems with the (additional) I/O load it generates.
@pg-gadfly's link answered it for me:
> VACUUM creates a substantial amount of I/O traffic, which can cause poor performance for other active sessions.
Another problem is that vacuum full compacts the database, it reduces database size on disk, but doesn't mean it makes database faster, often it might actually reduce performance.
Vacuum full might be useful in scenarios where you want to reclaim space, because autovacuum was not tuned correctly and tables get out of hand, if you need to run vacuum full frequently you should tune autovacuum to run more frequently.
Not saying do it all the time, but it is important to use surgically. You can easily calculate wasted table space
https://www.citusdata.com/blog/2017/10/20/monitoring-your-bl...
When it gets to be too big, it's a good time to do a vacuum full. I normally do it a few times a year on a few giant tables. Yes it locks up a table for a few minutes, but it is a big win and totally worth it.
In general, if you don't have extremely high UPDATE rates (thousands per second) you don't really need to care about vacuum these days (assuming you correctly end all your transactions and don't have any sessions in "idle in transaction" for a long time)
https://www.postgresql.org/docs/12/runtime-config-autovacuum...
And if you want to play with the Autovacuum settings, you really should read up on this in detail. There are some counter intuitive aspects to it, e.g. in many cases if you have problems with autovacuuming you should vacuum more, not less.
One exception is after you restore a backup, you might want to run "VACUUM ANALYZE" so that you get some good statistics immediately. That also will happen in the background later, but until then you can get weird query plans.
https://lob.com/blog/supercharge-your-postgresql-performance...
It might not be technically pure, but you absolutely can do this, and if your load is really asymmetrical and much quieter at night, then it may well make sense. It may also be unnecessary, as there is an auto-vacuum feature.
What PostgreSQL has though is micro vacuum where PostgreSQL tries to remove any no longer visible rows when it already has to modify a page. But it only does this as long as no index updates are necessary.
If a dead record is encountered in a scan, it is queued up to cleaned up by a background task, that runs every 5 seconds. This typically happens soon enough that the record is still cached, so no extra read I/O is needed, and in any case the location of the record is already known, so no extra scans are needed.
To handle other records, it maintains a bitmap of pages with ghost records. Each time it is awakened, it cleans up the queue, and then does up to 10 pages indicated in the bitmap. Then it goes back to sleep, and only wakes up again 5 seconds later.
This super slow cleanup of dead records that have not been recently scanned helps to avoid overloading the system, but does ensure cleanup will eventually occur, even if the table never gets scanned.
Pedantic note: (Technically it is a bytemap, as the bit is part of a larger structure, that holds other flags about a page.)
I haven't upgraded but would it actually be worth the effort?
I've got pgBackrest set up for backups and pgBouncer for connection pooling set up so it's a case of wondering if there is some benefit to the newer versions for someone using it "casually"
Since it's require quite a bit of reconfig and testing.
EDIT: forgot, pgBouncer also allows you to do hitless restart/upgrade, which is useful at any query volume I suppose ;)
Also the server the DB is on is RAM constrained. And Postgres making a process per connection brings it right down versus using pgBouncer to pool connections.
It seems complicated but it's actually quite simple.
But it's a good idea to keep up with the versions anyway. The upgrade processes isn't really complicated and if you can afford a downtime not really challenging.
In my experience it's typically easier to migrate from version x to x+1, rather than waiting a long time and then migrate from x to x+5 (or similar big jumps).
But regardless of the strategy for major version upgrades (10 -> 11 -> 12) you should at least keep up with the minor updates (10.1 -> 10.12)
Overall I love PostgreSQL but the vacuum/MVCC system of MSSQL and Oracle is much better suited for heavy writes, PostgreSQL needs a lot of tweaking in table structures to handle those.
I hoped to see the "pluggable storage" show up in Postgres 13 (including zheap which uses UNDO logs) but apparently that didn't make it.