MVCC
Readers never block writers because each sees its own snapshot of history.
The mechanism. Instead of overwriting a row, the database writes a new version of it, tagged with the transaction that created it. Every transaction gets a snapshot — a rule for which versions are visible to it. A reader walks the version chain and picks the newest version visible under its snapshot. Writers append; they do not block readers, and readers do not block writers.
The cost nobody plans for. Old versions must be collected. In PostgreSQL that is VACUUM, and a long-running transaction pins every version created since it started, because that transaction might still need to see them. One forgotten BEGIN in a psql session can bloat a table by gigabytes and make the planner's statistics useless. In MySQL/InnoDB the same problem appears as a growing undo log and "history list length".
Snapshot isolation is not serializable. Two transactions can each read a consistent snapshot, each write a disjoint row, and together break an invariant neither broke alone — write skew. The textbook case is two doctors both dropping their on-call shift because each sees the other still on call. Fix it with SELECT ... FOR UPDATE, a serializable isolation level, or a constraint the database can check.
Takeaway. MVCC is why your reads are fast and why your table is 40GB for 2GB of rows.