SQL Databases (PostgreSQL, MySQL) — ACID, Joins, Strong Consistency

9 min read

Reading Progress0%
System Design Index
Start Here
Tier 1 -- Building Blocks
Tier 2 -- Core Systems
URL Shortener — System Design InterviewFree
Pastebin — System Design InterviewFree
News Feed System — System Design InterviewFree
Chat System (WhatsApp/Messenger) — System Design InterviewFree
Tier 3 -- Location & Real-Time
Tier 4 -- Infrastructure & Data
Tier 5 -- Finance & Commerce
Tier 6 -- Advanced & Collaborative
System Design Index
Start Here
Tier 1 -- Building Blocks
Tier 2 -- Core Systems
URL Shortener — System Design InterviewFree
Pastebin — System Design InterviewFree
News Feed System — System Design InterviewFree
Chat System (WhatsApp/Messenger) — System Design InterviewFree
Tier 3 -- Location & Real-Time
Tier 4 -- Infrastructure & Data
Tier 5 -- Finance & Commerce
Tier 6 -- Advanced & Collaborative

SQL Databases (PostgreSQL, MySQL) — ACID, Joins, Strong Consistency

Tier 1 — Building Block


1. What Is It?

A relational database stores data in tables with predefined schemas, enforces relationships between tables (foreign keys), and provides SQL as a query language for flexible data retrieval including multi-table JOINs, aggregates, and subqueries. The defining property is ACID: Atomicity, Consistency, Isolation, — a guarantee that transactions execute as all-or-nothing, leave the database in a valid state, are isolated from concurrent transactions, and survive crashes.

SQL databases are the default starting point for most application data. They handle reads, writes, complex queries, and transactional consistency on a single node with battle-tested . The question is never "should I use a relational database?" — it's "when do I need to move beyond one?"


QUICK CHECK

A payment processing service is transferring $500 from a user's checking account to their savings account. Midway through the operation, the database server crashes. When it recovers, the $500 has been deducted from checking but never credited to savings. Which ACID property was violated?

Choose one answer

2. How It Works

ACID Properties

PropertyWhat It MeansMechanism
AtomicityAll operations in a transaction succeed, or none doWrite-Ahead Log (WAL) — on crash, incomplete transactions are rolled back by replaying WAL
ConsistencyDatabase moves from one valid state to another (constraints, foreign keys respected)Constraint checking at commit time; referential integrity enforcement
IsolationConcurrent transactions don't see each other's incomplete workMVCC (Multi-Version Concurrency Control) — each transaction sees a snapshot of data
DurabilityCommitted transactions survive crashesWAL flushed to disk (fsync) before commit acknowledgment

MVCC (Multi-Version Concurrency Control)

Both PostgreSQL and MySQL/InnoDB use MVCC to allow concurrent reads and writes without locking.

MVCC = readers don't block writers; writers don't block readers. The tradeoff: old versions accumulate and must be periodically cleaned up (VACUUM in PostgreSQL, InnoDB purge in MySQL).

Query Execution: Index Lookup

PostgreSQL vs. MySQL Key Differences

FeaturePostgreSQLMySQL (InnoDB)
Data typesRicher: JSONB, arrays, ranges, enums, custom typesStandard SQL types; JSON (less optimized than PG)
JSONBFirst-class JSONB with GIN indexes — powerful hybridJSON column type, limited indexing
Full-text searchBuilt-in FTS with tsvector/tsquery, GIN indexesFull-text indexes, less flexible than PG
ReplicationStreaming WAL replication (physical) + logical replicationBinlog-based; Group Replication; InnoDB Cluster
VACUUMExplicit periodic vacuum for dead tuple cleanupBackground purge thread (more automatic)
Concurrent DDLLimited — many DDL operations lock the tableMore online DDL support (ALTER TABLE non-blocking for some ops)
PerformanceGenerally faster for complex queries; better query plannerGenerally faster for simple read/write; better at high-connection-count workloads
Community vs. ecosystemOpen-source; owned by communityOpen-source core; Oracle-backed (MySQL); MariaDB fork
Best forComplex queries, JSONB, analytics, geospatial (PostGIS)High-connection OLTP, read replicas at massive scale, web apps

QUICK CHECK

Transaction T1 begins and takes a snapshot of the database at timestamp 100. While T1 is still running, Transaction T2 updates a row and commits at timestamp 101. When T1 subsequently reads that same row, what value does it see and why?

Choose one answer

3. Variants & Comparisons

Isolation Levels

SQL standard defines 4 isolation levels. Higher isolation = fewer anomalies, more locking/blocking.

LevelDirty ReadNon-Repeatable ReadPhantom ReadCommon Use
Read UncommittedPossiblePossiblePossibleAlmost never used
Read CommittedNot possiblePossiblePossiblePostgreSQL default
Repeatable ReadNot possibleNot possiblePossibleMySQL InnoDB default (prevents phantom reads too via gap locks)
SerializableNot possibleNot possibleNot possibleFinancial transactions; correctness-critical operations

PostgreSQL implements MVCC-based Serializable Snapshot Isolation (SSI) — full serializability without pessimistic locking.

When SQL Scales (with standard techniques)

ChallengeSolutionScale Ceiling
Read throughputRead replicas10–15× read scale
Write throughputVertical scale (bigger instance)~100K writes/sec on largest instances
Dataset sizeVertical scale + tablespace~10TB practical per node
Complex queriesQuery optimization, partial indexes, materialized viewsDepends on query complexity
Connection overloadPgBouncer/ProxySQL connection pooling (sharing a pool of database connections across many app threads, instead of each opening its own)Handles thousands of app-level connections

When SQL Doesn't Scale

ConstraintSymptomSolution
Write throughput ceilingPrimary CPU > 90% on writes at peakSharding (functional decomposition first; hash/range sharding if needed)
Dataset size > node capacityDisk I/O bottleneck; B-tree depth increasesSharding, archival, tiered storage
Schema flexibility neededFrequent ALTER TABLE migrations as data evolvesDocument store (MongoDB) or JSONB columns
Massive horizontal write scale500K+ writes/secWide-column store (Cassandra) or NewSQL

QUICK CHECK

A fintech application processes bank transfers and requires that concurrent transactions produce results identical to running them one at a time — no dirty reads, no non-repeatable reads, and no phantom reads. However, the team wants to avoid the performance bottleneck of traditional pessimistic locking. Which isolation level and implementation approach satisfies both requirements?

Choose one answer

4. When to Use It (and When NOT To)

Use SQL When:

  • Relational data with JOINs: Users have orders; orders have items; items have categories — relational structure maps naturally to SQL tables.
  • ACID transactions required: Transfer money between accounts; place an order that atomically reduces inventory and creates a payment — multi-table atomicity is a SQL strength.
  • Complex ad-hoc queries: Analytics, reporting, "give me all users who bought product X in the last 30 days who haven't bought product Y" — SQL is expressive and flexible.
  • required: Financial balances, booking reservations, inventory counts — you cannot tolerate stale reads.
  • Most web applications: CRUD operations, user records, content management — SQL handles 95% of web application data needs.

Decision triggers:

  • "Default to PostgreSQL unless you have a specific reason to choose otherwise"
  • "If you need ACID transactions that span multiple tables → SQL"
  • "If your data is relational (entities with relationships) and fits within one node → SQL"

Do NOT Use SQL When:

  • Massive write (>100K writes/sec sustained): A single SQL node can't keep up — use Cassandra, , or shard.
  • Very large unstructured/semi-structured data: 10+ columns that are frequently null or vary per record — use a document store or add JSONB columns (PostgreSQL handles this better than most).
  • Pure key-value lookups with sub-ms requirement: Use Redis; SQL can't match in-memory .
  • Hierarchical or graph-heavy relationships: 6+ levels of self-referential joins kill SQL performance. Use a graph database (Neo4j).

Anti-patterns:

  • N+1 queries: Loading N parent records and then making N separate queries for their children. Fix: JOIN in one query or use WHERE id IN (...) batch query.
  • SELECT * in production: Fetches all columns including large ones (BLOB, TEXT) you don't need. Always SELECT only needed columns.
  • No indexes on foreign keys and common WHERE columns: Table scans kill performance at scale. EXPLAIN ANALYZE every slow query.
  • Large transactions holding locks: A transaction that holds a row lock for seconds while doing business logic blocks all concurrent writers. Keep transactions short.
  • Using SQL for queue/polling pattern: SELECT ... FOR UPDATE SKIP LOCKED works but is inefficient at scale compared to a real queue (Redis, SQS). Don't build a job queue in PostgreSQL unless it's truly low-volume.

QUICK CHECK

Your team is building an e-commerce backend. When a customer places an order, the system must simultaneously reduce inventory counts, create an order record, and record a payment — all as a single atomic operation. Which database choice is most appropriate for this requirement?

Choose one answer

5. Real-World Usage

Instagram (PostgreSQL at scale): Instagram ran PostgreSQL as their primary database for years, by user_id at the application layer. They used PostgreSQL for user profiles, photos metadata, follower graphs (early), and direct messages. When they hit the single-node write ceiling, they sharded horizontally — but kept PostgreSQL as each shard's engine. As of Meta's ownership, Instagram's core relational data still runs on PostgreSQL.

GitHub (MySQL at massive scale with Vitess): GitHub uses MySQL as their primary database, scaled with read replicas and eventually Vitess for horizontal . GitHub's "gh-ost" tool (GitHub's online schema change tool for MySQL) was open-sourced because large-scale MySQL table migrations are a genuine operational challenge. GitHub serves ~100M developers from a MySQL + Vitess core.

Airbnb (MySQL + PostgreSQL): Airbnb uses MySQL for its core transactional data (bookings, payments, users) and PostgreSQL for specific use cases needing JSONB, advanced indexes, or PostGIS (geospatial). Their booking system requires strict ACID transactions — a failed booking must atomically return inventory and void the payment. SQL's guarantees are non-negotiable here.


QUICK CHECK

Airbnb's booking system requires that when a booking fails, inventory is returned and the payment is voided together — either both happen or neither does. Which database property makes a relational database the appropriate choice for this requirement?

Choose one answer

6. Interview Cheat Sheet

Key sentences to demonstrate depth:

  1. "PostgreSQL is my default. ACID guarantees, mature query planner, JSONB for semi-structured data, -based streaming , and decades of production hardening. I only deviate when a specific constraint (write , schema flexibility, graph queries) pushes me elsewhere."
  2. "MVCC is how PostgreSQL achieves high concurrency: each transaction sees a snapshot of the database at its start time. Readers don't block writers; writers don't block readers. The cost is VACUUM — periodic cleanup of dead tuple versions."
  3. "For ACID transactions spanning multiple tables: BEGIN / your operations / COMMIT. If anything fails, ROLLBACK. This is the correct tool for 'charge the credit card AND reduce inventory AND create the order' — all or nothing."
  4. "The SQL scaling ladder: (1) Add indexes, (2) Connection pool (PgBouncer), (3) Read replicas for read scale, (4) Vertical scale, (5) Functional decomposition (split by service), (6) Horizontal . Exhaust each step before moving to the next."
  5. "The N+1 query problem is the most common SQL performance issue in practice: a loop in application code issues one query per item instead of batching. Fix with JOINs or WHERE IN (ids) batching."

Common follow-up questions:

QuestionConcise Answer
"What is VACUUM in PostgreSQL?"PostgreSQL's MVCC leaves behind dead tuple versions (old rows after UPDATE/DELETE). VACUUM reclaims disk space and prevents transaction ID wraparound (a critical maintenance operation). autovacuum handles this automatically but can fall behind on write-heavy tables.
"PostgreSQL vs. MySQL — which do you prefer?"PostgreSQL for complex queries, JSONB, analytics workloads. MySQL for high-connection OLTP, teams with existing MySQL expertise, or ecosystems tightly coupled to MySQL (WordPress, many ORMs). Both are excellent; team knowledge often drives the choice.
"What is an index and why does it help?"A B-tree index is a sorted copy of a column (or columns) that enables O(log N) binary search instead of O(N) table scan. The cost: ~10–20% write overhead to maintain the index on every INSERT/UPDATE.
"What is connection pooling and why is it needed?"Each PostgreSQL process uses ~5–10MB RAM. 10,000 connections = ~100GB RAM just for connections. PgBouncer sits in front and multiplexes many application connections through a smaller pool of actual database connections.
"When would you choose NoSQL over PostgreSQL?"When write throughput exceeds ~100K/sec (Cassandra), when you need sub-ms latency (Redis), when your data model is highly variable schema or document-oriented (MongoDB), or when you need global distributed ACID (Spanner/CockroachDB).

Connections to other building blocks:

  • Write-Ahead Log: PostgreSQL and MySQL use for both crash recovery (ACID ) and (streaming WAL to replicas). WAL is the foundation of SQL database .
  • Read Replicas: SQL databases scale reads via streaming replication to read replicas. PostgreSQL streaming replication is WAL-based; MySQL uses binlog replication.
  • : SQL databases can be sharded (Instagram, GitHub), but it must be done at the application layer since most SQL databases don't shard natively. NewSQL (Spanner, CockroachDB) adds automatic sharding.
  • Caching: Redis in front of PostgreSQL is the most common scaling pattern — absorb hot reads in Redis, keep the DB for writes and complex queries.
  • CAP Theorem: SQL databases are CP (Consistency + Partition Tolerance). They sacrifice : if the primary is down, writes fail (unless you have automatic failover).
Glossary History

Click dotted jargon to save explanations here.

Glossary History

Click dotted jargon to save explanations here.