Chapter 1: What is a Database and Why Do We Need One?
The Problem: Storing Data That Outlives Your Program
As a C programmer, you know that when your program exits, all its variables disappear. The memory is freed. Everything in RAM is gone.
// This data dies when the program exits
struct User {
int id;
char name[64];
char email[128];
};
struct User users[1000]; // Gone when process terminates
So you write to a file:
FILE* f = fopen("users.dat", "wb");
fwrite(users, sizeof(struct User), 1000, f);
fclose(f);
This works for simple cases. But what happens when:
- Two programs need to read/write the same data simultaneously?
- Your program crashes mid-write — is the file corrupted?
- You have 10 million users and need to find one by email?
- You need to update one field without rewriting the entire file?
- You need to ensure "transfer $100 from A to B" either fully completes or fully rolls back?
These are the problems a database solves.
Definition
A database is an organized collection of data stored and accessed electronically. A Database Management System (DBMS) is the software that manages that data — handling storage, retrieval, updates, concurrency, crash recovery, and security.
What a Database Gives You (That Files Don't)
| Problem | File Approach | Database Approach |
|---|---|---|
| Find one record in millions | Scan entire file: O(n) | Index lookup: O(log n) |
| Concurrent access | File locks (coarse, slow) | Row-level locks, MVCC |
| Crash mid-write | Corrupted file | Write-ahead log → auto-recovery |
| Partial update | Rewrite entire file | Update in-place or append |
| Atomic multi-step ops | Manual (error-prone) | Transactions (all-or-nothing) |
| Query flexibility | Write custom code per query | SQL: declarative queries |
| Data integrity | Manual validation | Constraints, foreign keys |
The Core Guarantees: ACID (Preview)
Every serious database provides these four guarantees (we'll deep-dive in Chapter 17):
- Atomicity — A transaction either fully completes or fully rolls back. No half-done operations.
- Consistency — Data always satisfies defined rules (constraints, foreign keys).
- Isolation — Concurrent transactions don't interfere with each other.
- Durability — Once committed, data survives crashes and power failures.
Think of ACID like signaling in telecom. When a UE does a Registration, the AMF must either complete the entire procedure (update context, notify SMF, etc.) or roll back completely. You can't have a half-registered UE. That's atomicity. A database gives you this guarantee automatically for any operation you wrap in a transaction.
A Taste of SQL
Instead of writing C code to search through arrays, you write declarative queries:
-- Find a user by email (database figures out HOW to find it efficiently)
SELECT id, name, email
FROM users
WHERE email = 'alice@example.com';
-- Transfer money atomically
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- If anything fails between BEGIN and COMMIT, BOTH updates are rolled back
Compare to the C equivalent:
// Without a database, YOU must handle all of this:
int transfer(int from, int to, int amount) {
lock_file(accounts_file); // concurrency
read_all_accounts(accounts_file); // load data
if (accounts[from].balance < amount)
return -1; // validation
accounts[from].balance -= amount;
accounts[to].balance += amount;
// What if we crash HERE? File is half-written!
write_all_accounts(accounts_file); // persist
unlock_file(accounts_file); // release
return 0;
}
Client-Server Architecture
Most databases run as a separate process (a server/daemon). Your application connects to it over a network protocol:
This separation means:
- Multiple applications can share the same database
- The database can run on dedicated hardware
- The database manages its own memory, caching, and I/O
- Your app doesn't need to worry about file formats or corruption
Embedded vs Client-Server
Not all databases are client-server. Some are embedded — they run inside your process:
| Type | Examples | Use Case |
|---|---|---|
| Client-Server | PostgreSQL, MySQL, Oracle | Multi-user apps, web services |
| Embedded | SQLite, RocksDB, LevelDB | Mobile apps, single-process, local storage |
SQLite is the most deployed database in the world (billions of instances — every phone, every browser). It's a single C file you compile into your application. No server, no configuration. Perfect for learning SQL and for embedded use cases.
The Big Picture: Where Databases Fit
Summary
- A database = organized persistent storage + DBMS software to manage it
- Solves: concurrent access, crash recovery, fast lookups, atomic operations, data integrity
- ACID guarantees make multi-step operations safe
- SQL = declarative language to query/modify data (you say WHAT, DB figures out HOW)
- Client-server (PostgreSQL) vs embedded (SQLite) — different use cases
- In microservices, each service typically owns its own database