SQL and NoSQL aren't competitors — they solve fundamentally different problems. Engineers who treat this as a preference debate end up rewriting their data layer when traffic hits.

What's actually happening here?

SQL databases store data in tables — rows and columns, like a spreadsheet, with strict rules about what shape data must take. NoSQL databases are a family of storage systems that deliberately break one or more of those rules to gain something in return — speed, flexibility, or the ability to scale horizontally without redesigning your schema. The word "NoSQL" doesn't mean "no SQL syntax" — it means "not only SQL", and it covers four completely different storage models.

The problem this solves

Relational databases were designed in the 1970s for structured business data — invoices, accounts, inventory. They're exceptional at enforcing relationships and answering complex queries across multiple tables. But when you're storing 500 billion tweets, or tracking the real-time location of 5 million delivery drivers, or indexing every word in every document on the internet — a relational table is the wrong shape for the job. NoSQL databases exist because different data access patterns need different storage architectures.

How it really works — the four NoSQL families

1. Key-value stores (Redis, DynamoDB)
The simplest model: every piece of data has a unique key, you retrieve it by that key. Nothing else. No filtering by value, no sorting, no joining. In exchange: O(1) reads and writes regardless of dataset size. Used for session storage, caching, rate limiting counters, and any data you always access by a known ID.

2. Document stores (MongoDB, Firestore)
Store self-contained JSON objects — called documents — where each document can have a different structure. No rigid schema enforced. A user document might have 3 fields; another might have 30. Flexible for evolving data models, but dangerous when you need to query across fields that only exist on some documents.

3. Wide-column stores (Cassandra, HBase)
Tables with rows and columns like SQL — but each row can have different columns, and the data is physically stored column-by-column on disk rather than row-by-row. Designed for write-heavy workloads at massive scale. Cassandra accepts writes at 1 million/second per node because it never updates in place — it only appends. Used by Instagram for activity feeds, Discord for message history, Netflix for viewing history.

4. Graph databases (Neo4j, Amazon Neptune)
Data stored as nodes connected by edges, each with properties. Designed for relationship traversal — "find all friends of friends who live in Mumbai and bought the same product." A SQL query for this would require multiple self-joins and get exponentially slower with depth. A graph database traverses the same query in constant time regardless of graph size.

The part most tutorials skip

The real difference is the write path, not the query language. SQL databases write to disk using B-tree indexes — they update data in place, which requires finding the page, loading it into memory, modifying it, and writing it back. This is random I/O, which is slow on spinning disks and expensive on SSDs.

Cassandra and other LSM-tree databases (RocksDB, LevelDB) never update in place. Every write is an append to an in-memory structure (memtable), flushed sequentially to disk as an immutable file (SSTable). Sequential writes are 10–100× faster than random writes. The trade-off: reads are slower because data for one key may be spread across multiple SSTables, and compaction runs periodically to merge them. This is precisely why Cassandra handles 1 million writes/second where Postgres handles tens of thousands.

Real company doing this right now

Discord stores 4 billion messages per day in Cassandra — partitioned by channel ID so all messages in a channel land on the same node, making reads fast. The partition key is (channel_id, bucket) where bucket is a time-based integer — this spreads write load across time while keeping channel messages co-located. When Discord added reactions, they stored them in the same Cassandra cluster. When they later needed fast aggregation queries (how many of each reaction per message?), Cassandra's data model made that expensive — so they moved reactions to a separate Redis sorted set. One product, two databases, each chosen for its specific access pattern.

What breaks at scale?

NoSQL's flexibility becomes a liability when queries change. Cassandra's golden rule: design your tables around your queries, not your data. If you design a Cassandra table for "fetch messages by channel", it's extremely fast. If you later need "fetch all messages by a user across all channels" — that query is catastrophically expensive because the data is partitioned by channel, not by user. You'd need a second table, denormalised for the new access pattern. SQL would handle both queries on one table with two indexes. This is the hidden cost of NoSQL: schema design is harder, not easier, because you must know your access patterns upfront.

The "aha" moment

SQL enforces consistency at write time — it rejects data that breaks the rules. NoSQL enforces nothing and accepts everything — which makes it fast and flexible, but moves all the data quality responsibility to your application code. At scale, bugs in that application code corrupt data across billions of rows with no automatic protection.

Your practical takeaway

  • Default to PostgreSQL for anything with relationships, transactions, or unknown query patterns — it handles 95% of use cases, and you can always add a specialised database later when a specific bottleneck appears.

  • Add Redis alongside your SQL database from the start — not to replace it, but as a cache layer for frequently-read data and as a fast store for ephemeral data like sessions, rate limit counters, and presence indicators.

  • Only choose Cassandra or another wide-column store when you have a write-heavy workload with a known, stable access pattern — and you've already maxed out what Postgres can do with sharding and read replicas.

Lesson 07 · Stage 2 — Storage Architecture · System Design Made Easy