Databases Tutorial with Examples.
Database tutorial for developers: design, query and operate modern databases, from schema design and indexing to transactions and performance.
Intermediate
For developers with core concepts down
Choosing a Database: A Decision Framework With Real Numbers
"It depends" is true and useless. A weighted scoring framework — consistency, query flexibility, write latency, schema flexibility — run against three real scenarios to three different, honest winners.
Transactions Across Engines: Who Can Actually Roll Back
PostgreSQL and MongoDB both undo a partially-applied multi-step change when one step fails. Redis's MULTI/EXEC does not — a real queued transaction that keeps its successful command and drops only the failed one.
Advanced
Production-grade patterns for experienced engineers
Indexing Across Engines: One B-Tree Idea, Measured Twice
The same customer_id lookup, on the same shape of 100k+ row table, measured before and after an index in both PostgreSQL and MySQL — two engines, two real query planners, the same order-of-magnitude win.
ACID vs BASE: the Lost Update Neither Engine Prevents by Default
A naive read-then-write race loses an update in both PostgreSQL and Redis when neither uses locking — the real difference is that Postgres ships a built-in fix (SELECT ... FOR UPDATE) and a raw key-value GET/SET pair has no equivalent.
Scaling Patterns: Real Sharding vs a Table That Only Looks Sharded
MongoDB's sharding puts data on genuinely separate machines; MySQL's PARTITION BY divides a table by the same kind of range key but keeps every partition on one server. Both engines' own distribution-reporting tools also turn out to report the wrong numbers.
Normalized vs Embedded: a Tradeoff That Points in Two Different Directions
Fetching one customer with their orders costs about the same, indexed, in both a normalized PostgreSQL schema and an embedded MongoDB document. Searching across orders independent of their customer is measured 11x slower embedded — the same schema choice wins and loses depending which direction you query.
Also part of Databases
Taught in other topics. Each link opens the lesson in its home topic.
Schema Design
Design relational schemas with real PostgreSQL output: an update anomaly and its fix, junction tables, soft deletes, and UUID vs BIGINT keys.
Indexing Strategy: When an Index Actually Helps
Indexing a low-selectivity column barely moves the needle — 13ms to 13ms — while indexing a high-selectivity one drops a query from 14ms to 1ms. Selectivity, not the presence of an index, is what determines the payoff.
Composite Indexes: The Filesort a Single-Column Index Can't Avoid
Filtering by genre and sorting by year: a single-column index still needs a manual sort afterward (2.87ms). A composite index on both columns delivers pre-sorted results — measured 21x faster, and the Sort step vanishes from the plan entirely.
Clustered vs Nonclustered: When an Index Makes a Query Slower
Forcing SQL Server to use a nonclustered index costs 217 logical reads -- worse than the 40-read scan it was supposed to beat -- because every matching row needs a separate key lookup. INCLUDE fixes it at 4 reads.
A Real Deadlock, and Why Lock Order Fixed It
One error inside a transaction poisons every statement after it until ROLLBACK — and two transactions locking the same two rows in opposite order produces a real 'deadlock detected' error, not a hang.
Isolation Levels: The Same Transaction Reading Two Different Values
Under MySQL's default REPEATABLE READ, a transaction reads the same value twice even after another session commits a change in between. Switch to READ COMMITTED and the second read sees the new value — a real, measured non-repeatable read.
A Real Dirty Read: Seeing a Value That Never Actually Existed
READ UNCOMMITTED reads an uncommitted price change mid-transaction -- then the transaction rolls back, and the value never really existed. The default READ COMMITTED blocks instead, confirmed with a real timeout.
Replication: A Second Server, and the read_only Gap Nobody Mentions
A real primary-replica setup, checked with SHOW REPLICA STATUS instead of assumed — plus a genuine surprise: read_only alone does not stop root from writing to a replica.
Killing the Primary and Measuring the Real Failover Time
docker kill the primary node of a real 3-node replica set and time exactly how long the cluster takes to elect a new one from container logs and MongoDB's own timestamps — 9.01 seconds, closely matching the documented default election timeout.
Point-in-Time Recovery: Undoing a Bad DELETE That Already Committed
A base backup plus archived WAL segments can restore a database to the exact second before a mistake — but only if the WAL covering that second was actually archived first, which is the real failure this lesson walks through.
Backup and Recovery: What a Restore Actually Gives Back
A real mysqldump, a real DELETE with no WHERE clause, and a real restore — which undoes the mistake but also erases legitimate work done after the backup, exposing exactly what a full logical restore can't do alone.
Quorum Consistency: When a Read Is Guaranteed to See the Latest Write
A write acked by W replicas and a read from R only guarantee overlap when W plus R exceeds the replica count -- otherwise a read can miss the latest write.