PostgreSQL#
Relational Strong consistency Open source
What it is#
PostgreSQL is an open source, object-relational database that's been under active development since the 1980s (as POSTGRES, then Postgres95, then PostgreSQL). It's known for strict standards compliance, genuine extensibility, and a reputation for not losing your data even when things go wrong.
How it works under the hood#
Postgres uses a process-per-connection model: every client connection gets its own backend process, not a thread. It stores data in on-disk pages grouped into tables, uses a write-ahead log (WAL) for durability and crash recovery, and relies on MVCC (multi-version concurrency control) so readers never block writers and writers never block readers.
What MVCC actually buys youInstead of locking a row for every read, Postgres keeps multiple versions of a row around.
Instead of locking a row for every read, Postgres keeps multiple versions of a row around. A transaction sees a consistent snapshot of the database as of when it started, regardless of what other transactions are doing at the same time. Old row versions get cleaned up later by a background process called autovacuum.
Setting it up#
brew install postgresql@16
brew services start postgresql@16
createdb myapp
docker run --name pg -e POSTGRES_PASSWORD=secret -p 5432:5432 -d postgres:16
sudo apt install postgresql postgresql-contrib
sudo -u postgres createuser --interactive
sudo -u postgres createdb myapp
Your first queries#
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
INSERT INTO users (email) VALUES ('ada@example.com');
SELECT id, email FROM users WHERE created_at > now() - interval '7 days';
Functionality that sets it apart#
Indexing options#
Index type | Good for | Example |
|---|---|---|
B-tree | Default, equality and range queries |
|
GIN | JSONB, arrays, full-text search |
|
GiST | Geometric data, ranges, nearest-neighbor | Used by PostGIS internally |
BRIN | Huge tables with naturally sorted data (timestamps) | Time-series tables with billions of rows |
In production#
-- Check what autovacuum is doing right now
SELECT relname, last_autovacuum, n_dead_tup
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 5;
Warning
The single most common Postgres production issue isn't a bug, it's max_connections being exhausted because nobody put a pooler like PgBouncer in front of it. Postgres backends are real OS processes, and they aren't free.
Replication is built in via streaming WAL replication for read replicas and failover, and logical replication for selective, cross-version replication between databases.
Pros and cons#
Rock-solid ACID guarantees
Enormous extension ecosystem
Excellent JSON support without giving up SQL
Free, no licensing games
Vertical scaling has a ceiling, and sharding isn't built in
Connection overhead means pooling is mandatory at scale
VACUUMtuning is a real skill you have to learnReplication lag can bite naive read-replica setups
When to reach for it#
Tip
If you're not sure what database to start with, Postgres is almost always the right default. It scales further than people expect before you need anything more exotic.
Good fits: general application backends, financial data, anything where data integrity matters more than raw write throughput, and workloads that benefit from rich querying (joins, window functions, CTEs) over simple key lookups.
When it's not the right fit#
Extremely high write throughput with simple access patterns (consider Cassandra)
Sub-millisecond cache-style lookups (consider Redis)
Massive horizontal write scaling across regions without a lot of operational investment (consider a distributed SQL system)
connection-pooling for handling Postgres's per-connection process model at scale, and caching-strategies for offloading hot reads.