3B Infrastructure home

Database Concepts

How data is modelled, kept safe and spread across machines. These ideas apply whichever database you end up using. Press play on any sketch to watch it happen.

Modelling data

1 Tables, keys and relationships

A relational database stores data in tables of rows and columns. Keys connect the tables, so each fact lives in one place and queries join them back together.

  • Primary key: a column that uniquely identifies each row.
  • Foreign key: a column that points at a row in another table. The database can reject values with no match.
  • One-to-many: one customer has many orders, so orders hold the customer id.
  • Many-to-many: students and courses, which need a join table in the middle.
SELECT c.name, COUNT(o.id) AS orders
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.id, c.name;

Entity relationship diagram

customersid (PK)nameemailordersid (PK)customer_id (FK)totalmany to one

1/4 Each customer lives in one row, in one table.

Rule of thumb: Let the database enforce the rules. Constraints catch the bugs your application code will miss.

2 Normalization

Normalization organises tables so each fact is stored once. That prevents anomalies like changing a customer's email in one row and missing the other twenty.

  • 1NF: each cell holds one value, with no lists or repeating groups.
  • 2NF: every non-key column depends on the whole primary key, not part of a composite key.
  • 3NF: non-key columns depend only on the key, not on each other.
  • Denormalization: copying data on purpose to speed up reads. Fair enough once you have measured a need and accept keeping the copies in sync.

Schema diagram

ordersid, item, customer_name, customer_email

1/5 One table repeats the customer on every single order.

Rule of thumb: Every column should depend on the key, the whole key, and nothing but the key.

Keeping data correct

3 Transactions and ACID

A transaction groups several steps into one unit that either fully happens or never happened. Moving money is the classic example: the debit and the credit must succeed together.

  • Atomicity: all the steps commit, or all of them roll back.
  • Consistency: constraints that held before the transaction still hold after it.
  • Isolation: concurrent transactions do not trip over each other, to the degree the isolation level promises.
  • Durability: once committed, the data survives a crash, usually thanks to a write-ahead log.
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

Flowchart

BEGINDebitaccount 1Creditaccount 2COMMIT

1/6 BEGIN opens the transaction. Nothing is final yet.

Rule of thumb: Keep transactions short. A long one holds locks or old row versions, and everyone else waits or pays for it.

4 Isolation levels

Stronger isolation gives you fewer surprises but allows less concurrency. The SQL standard defines each level by the anomalies it rules out.

  • Dirty read: you see another transaction's uncommitted change.
  • Non-repeatable read: reading the same row twice gives different values.
  • Phantom read: running the same query twice returns new rows.
  • Real engines differ: PostgreSQL defaults to read committed and MySQL InnoDB to repeatable read, and each can be stricter than the standard requires.

Sequence diagram

Transaction 1RowTransaction 2read: 100

1/4 Transaction 1 reads the row and sees 100.

LevelDirtyNon-repeatablePhantom
Read uncommittedpossiblepossiblepossible
Read committednopossiblepossible
Repeatable readnonopossible
Serializablenonono

Rule of thumb: Check your database's default level and what it actually guarantees. Do not assume the textbook table.

Speed and scale

5 Indexes

An index is a sorted lookup structure, like the index at the back of a book. Without one the database scans every row. With one it jumps to the right place.

  • B-tree: the usual default. Handles equality, ranges and sorting.
  • Hash: very fast for equality, no help with ranges.
  • Composite: an index on (country, city) helps filters on country or on both, but usually not on city alone.
  • The cost: every index uses disk space and slows down writes.
CREATE INDEX idx_orders_customer ON orders (customer_id);

Tree diagram

30 | 6010 | 2040 | 5070 | 80under 3030 to 60over 60

1/4 Find 40. With no index, the database would have to read every row.

Rule of thumb: Run EXPLAIN on a slow query before adding an index, and again afterwards to prove it gets used.

6 Replication

Replication keeps copies of your data on several servers. You gain availability and read capacity, at the cost of copies that can lag behind.

  • Primary and replicas: one node takes writes, the others follow it and serve reads.
  • Synchronous: the primary waits for a replica to confirm. Safer, slower.
  • Asynchronous: the primary confirms straight away. Faster, but a crash can lose the latest writes.
  • Replication lag: a user may not see their own change on a replica, so read from the primary when it matters.

Deployment diagram

AppPrimaryReplica 1Replica 2writescopycopyreads

1/5 The app sends every write to the primary.

Rule of thumb: Replication protects you from a dead server, not from a bad DELETE. You still need backups.

7 Partitioning and sharding

When one server cannot hold the data or keep up with the writes, you split the data. Partitioning divides a table into pieces, and sharding puts those pieces on different servers.

  • Range: A to H here, I to P there. Great for range queries, but risks hot spots.
  • Hash: hash the key to choose a shard. Spreads data evenly, but a range query touches every shard.
  • Shard key: the most important choice, and painful to change later.
  • The cost: joins and transactions across shards become slow or hard.

Data flow diagram

Router"Alice"Shard 1: A to HShard 2: I to PShard 3: Q to Z

1/4 Alice starts with A, so the router sends her row to shard 1.

Rule of thumb: Sharding is a last resort. Try indexes, caching, read replicas and a bigger machine first.

8 CAP and consistency

In a distributed database, a network partition will happen sooner or later. CAP says that during one you must choose: refuse some requests to stay consistent, or answer them and risk stale data.

  • Consistency: every read sees the latest write.
  • Availability: every request to a working node gets a response.
  • Partition tolerance: the system keeps running when nodes cannot reach each other.
  • Eventual consistency: replicas converge once writes stop. Fine for like counts, not for bank balances.
  • PACELC: even with no partition, you still trade latency against consistency.

Failure scenario

Node Ax = 2Node Bx = 1WriterReaderreplication

1/5 A write reaches node A.

Rule of thumb: You do not really pick two of three. Partitions are not optional, so the choice is C or A when one happens.

9 NoSQL families

NoSQL is not one thing. Each family gives up some relational features to be very good at one shape of data.

  • Key-value: Redis, DynamoDB. Fast lookups by key for caches and sessions.
  • Document: MongoDB. JSON-like records for data that varies in shape.
  • Wide-column: Cassandra. Huge write volumes, read by a known partition key.
  • Graph: Neo4j. Relationships come first, for networks and recommendations.

Comparison

Your appKey-valuesession:42Document{ json }Wide-columnGrapha -> b

1/4 Need one thing by key, very fast? Use key-value, such as a login session in Redis.

Rule of thumb: Default to a relational database such as PostgreSQL. Reach for NoSQL when you can name the limit you are hitting.