Skip to content

๐Ÿ—„๏ธ Database Internals

Explain in 2 min

A database's job is to store data durably and let many clients read/write it correctly at once. ACID captures the correctness guarantees; isolation levels are the dial that trades strictness for concurrency. Under the hood, indexes (usually B-trees) turn O(n) scans into O(log n) lookups at the cost of slower writes and more space. To scale, we replicate (copies for availability and read throughput) and partition/shard (split data across nodes for write throughput and size). The moment data lives on multiple nodes, CAP/PACELC forces a choice between consistency and availability/latency. SQL vs NoSQL is mostly a question of whether you need strong relational guarantees and ad-hoc queries, or horizontal scale and a flexible schema.


ACID [core]

Property Meaning
Atomicity a transaction is all-or-nothing; partial effects never persist
Consistency a transaction moves the DB from one valid state to another (constraints hold)
Isolation concurrent transactions don't corrupt each other's view
Durability once committed, data survives crashes (via write-ahead log)

Atomicity and durability are usually implemented with a write-ahead log (WAL): changes are logged before being applied, so a crash can be replayed or rolled back.


Isolation levels & anomalies [core]

Stricter isolation = fewer anomalies but less concurrency. The anomalies, weakest first:

  • Dirty read - read another transaction's uncommitted change.
  • Non-repeatable read - re-read a row and see a different value (someone updated it).
  • Phantom read - re-run a range query and see new/removed rows.
Level Prevents Still allows
Read Uncommitted - dirty, non-repeatable, phantom
Read Committed dirty non-repeatable, phantom
Repeatable Read dirty, non-repeatable phantom
Serializable all (behaves as if serial) - lowest concurrency

Default is usually Read Committed (Postgres, Oracle) or Repeatable Read (MySQL InnoDB). Reach for Serializable only when correctness demands it.


Indexing [core]

An index is a secondary structure that makes lookups fast without scanning the whole table.

Index type Strength Weakness Used by
B-tree / B+tree range + equality, sorted scans write amplification most RDBMS default
Hash O(1) equality no range queries in-memory, some engines
LSM-tree very fast writes (sequential) reads may check multiple levels Cassandra, RocksDB, LevelDB

Trade-off: every index speeds reads but slows writes (each write updates the indexes) and costs storage. Index the columns you filter/join/sort on - not everything. A composite index on (a, b) helps queries on a or a,b but not b alone (leftmost-prefix rule).

  • B-tree vs LSM-tree is a classic follow-up: B-trees update in place (read-optimized); LSM-trees append to memtables and flush sorted files, compacting later (write-optimized). Pick LSM for write-heavy ingest, B-tree for balanced/read-heavy.
  • See DSA/data-structures/tree/ for tree/BST/segment-tree code and Hashing for hash-index intuition.

Normalization vs denormalization [core]

  • Normalization removes redundancy by splitting into related tables (3NF). Fewer update anomalies, but reads need joins.
  • Denormalization duplicates data to avoid joins. Faster reads, but writes must keep copies in sync.

Rule of thumb: normalize for write-heavy transactional systems (OLTP); denormalize for read-heavy or analytical paths (OLAP), and for NoSQL where joins are expensive or unavailable.


SQL vs NoSQL [core]

SQL (relational) NoSQL
Schema fixed, enforced flexible / schemaless
Scaling vertical + read replicas horizontal by design
Queries rich ad-hoc joins access-pattern-first
Consistency strong (ACID) often eventual (BASE)
Fit transactions, reporting, relationships huge scale, simple access patterns, flexible docs

NoSQL families: key-value (Redis, DynamoDB), document (MongoDB), wide-column (Cassandra), graph (Neo4j). "NoSQL scales better" is only true when your access patterns are known up front - model the queries first.


Replication [core]

Keeping copies of data on multiple nodes for availability and read throughput.

  • Single-leader - one node takes writes, replicates to followers. Simple, no write conflicts; follower reads can be stale (replication lag).
  • Multi-leader - multiple write nodes (e.g. multi-region). Better write availability, but conflict resolution needed.
  • Leaderless (Dynamo-style) - write to several nodes, read from several, use quorums (W + R > N) for consistency.

Sync vs async replication: sync is durable but slow (wait for followers); async is fast but can lose the newest writes on leader failure. Replication lag is the source of "I wrote it but can't read it back" bugs - fix with read-your-writes routing.


Partitioning / sharding [core]

Splitting data across nodes so no single node holds it all - the key to write scaling. Strategies (range, hash, consistent hashing) and the rebalancing/hot-shard problems have a canonical home: ๐Ÿ‘‰ Database Sharding. Consistent-hashing intuition is in Hashing.

Replication and partitioning are orthogonal and usually combined: shard for size/writes, replicate each shard for availability/reads.


CAP / PACELC [advanced]

Once data is distributed, a network partition (P) forces a choice between consistency (C) and availability (A) - you can't have both during a partition. PACELC extends it: else (when there's no partition) you still trade latency (L) vs consistency (C).

This is a system-design cornerstone with a full treatment (plus consensus, quorums, sagas) here: ๐Ÿ‘‰ Distributed Systems.


MVCC & concurrency control [advanced]

Multi-Version Concurrency Control lets readers and writers not block each other: each write creates a new version of a row, and each transaction reads a consistent snapshot. Used by Postgres and MySQL InnoDB. It's the optimistic-flavored counterpart to lock-based control - see optimistic vs pessimistic locking in Concurrency & Parallelism.

Old versions accumulate and must be cleaned up (Postgres VACUUM) - a real operational gotcha.


Connection pooling [core]

Opening a DB connection is expensive (TCP + auth + session setup). A connection pool keeps a set of open connections and hands them out, capping total connections so the DB isn't overwhelmed. This is the DB-side analogue of a thread pool. Under-sizing throttles the app; over-sizing exhausts DB resources.


Common interview traps

  • Confusing consistency in ACID (constraints valid) with consistency in CAP (all nodes see the same data) - different meanings.
  • Saying "just add indexes" - indexes slow writes and cost space; they're a trade-off.
  • Assuming replicas are instantly consistent (replication lag is real).
  • Treating CAP as "pick 2 always" - the choice only bites during a partition; PACELC captures the normal-operation latency trade-off.
  • Choosing NoSQL "because it scales" without knowing the access patterns.