The same data, stored four different ways. Compare documents, key-value pairs, wide columns and graphs against the relational original — and see what each model makes easy and what it makes painful.
Overview
Quick Context
A non-relational database stores data in a shape other than fixed tables of rows and columns. The name "NoSQL" is a historical accident — it is best read as "not only SQL", since many of these systems now support SQL-like query languages.
Non-Relational Databases: Four Different Bargains
"NoSQL" is not one thing. It is four families of database, each dropping a different relational guarantee to buy something else.
The Four Families
Document (MongoDB, Couchbase) — self-contained JSON-like documents. Related data is embedded rather than joined, so one read returns everything.
Key–value (Redis, DynamoDB) — the simplest possible model: a key returns an opaque blob. Extremely fast, but you can only look things up by key.
Wide column (Cassandra, HBase) — rows keyed for distribution, where each row can hold different columns. Built for enormous write volume across many machines.
Graph (Neo4j) — nodes and edges as first-class citizens. Relationships are traversed directly instead of being recomputed by joins.
Schema flexibility cuts both ways
The headline benefit of a document store is that you can add a field to one document without altering anything else. For rapidly changing data, or genuinely heterogeneous records, that removes real friction: no migration, no downtime, no coordination.
The cost arrives later. There is still a schema — it has just moved from the database into every piece of code that reads the data. Six months in, a collection contains documents from four generations of the application, and something must handle all of them. The database will not tell you which documents lack the field you are about to read; it will return undefined and let you find out in production.
The mature answer is schema validation at the application boundary or in the database's own validation rules, plus a version field on every document. That gets you flexibility with a way to reason about what exists — which is most of what a relational schema was giving you.
Denormalisation Is the Point — and the Cost
Document stores deliberately duplicate data so a page can be served with a single read. That is genuinely faster. But you have re-created the update anomaly from the relational lab: change a customer's name and you must now find every document that copied it.
The rule of thumb: relational optimises for correct writes, non-relational optimises for fast reads. Choose based on which one your workload does more of, and how much inconsistency you can tolerate.
What "NoSQL" actually covers
"NoSQL" is a label for four quite different families of database that share only the fact that they are not relational. Comparing them as one thing is where most confusion starts.
Document stores (MongoDB, Couchbase, Firestore) hold JSON-like documents. A whole order — customer details, line items, shipping address — lives in one document and is read in one operation. The schema can vary between documents in the same collection.
Key-value stores (Redis, DynamoDB, Memcached) are a giant hash map: give a key, get a value. Extremely fast, very simple, and the value is opaque to the store. Sessions, caches, feature flags, rate limiters.
Wide-column stores (Cassandra, HBase, ScyllaDB) look table-ish but each row can have different columns, and the data is partitioned across many machines by key. Built for enormous write volumes and linear horizontal scaling.
Graph databases (Neo4j, Neptune) store nodes and the edges between them, and traverse relationships in constant time per hop. Social networks, fraud rings, recommendation paths, dependency graphs.
Family
Read pattern it optimises
Typical use
Document
Fetch one nested object by id
Product catalogues, user profiles, CMS
Key-value
Fetch one value by exact key
Caching, sessions, counters
Wide-column
Fetch a partition, write heavily
Event logs, time series, IoT
Graph
Traverse relationships many hops deep
Social, fraud, recommendations
Denormalisation is the point, and the cost
Relational design says store each fact once and join. Document design says store the data the way you read it, even if that means duplicating.
Embedding an order's line items in the order document means one read instead of a join — genuinely faster, and it scales horizontally because everything needed sits in one partition. Embedding the product name too means the order renders without touching the product collection at all.
Then the product is renamed, and 40,000 order documents contain the old name. You now own that consistency problem. Sometimes that is correct — the name at the time of the order is a historical fact worth preserving. Sometimes it is a bug you find out about from a customer.
The design question for every embedded copy: is this a snapshot or a reference? Snapshots should be embedded. References should be looked up, even when that costs a second query.
The same data, shaped the other way
A document store keeps a customer and their orders together in one record. Building that shape here, out of the relational tables, makes the trade concrete -- which questions get cheaper, which get more expensive, and what the schema stops guaranteeing.
query.sqlSQLite
-- First, the relational answer to "give me a customer and their orders":
-- a join, assembled at read time.
SELECT c.name, o.id AS order_id, o.placed, o.status
FROM customers c LEFT JOIN orders o ON o.customer_id = c.id
WHERE c.name = 'Ada'
ORDER BY o.placed;
-- Now the document shape. A document store would hold this as ONE
-- record per customer, orders nested inside. Built here with JSON so
-- you can see it:
SELECT json_object(
'id', c.id,
'name', c.name,
'city', c.city,
'orders', json_group_array(
json_object('id', o.id, 'placed', o.placed,
'status', o.status))
) AS document
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE c.name = 'Ada'
GROUP BY c.id;
-- ONE read, no join, everything the customer page needs. That is the
-- case document stores are built for, and it is a real advantage.
--
-- Now ask a question that cuts the other way: which customers bought a
-- monitor? Relationally it is a join away.
SELECT DISTINCT c.name
FROM customers c
JOIN orders o ON o.customer_id = c.id
JOIN order_items i ON i.order_id = o.id
JOIN products p ON p.id = i.product_id
WHERE p.name = 'Monitor';
-- In the document shape there is no orders collection to search -- the
-- orders live inside customer documents. Answering it means opening
-- every customer document and looking inside, which is why document
-- stores grow secondary indexes, and why the shape you chose at the
-- start decides which questions stay cheap.
--
-- And the guarantee that goes away: nothing checks that a nested order
-- has the fields the next reader expects.
SELECT json_object('id', 9, 'placed', '2024-09-01') AS doc_missing_status
UNION ALL
SELECT json_object('id', 10, 'placed', '2024-09-02', 'status', 'shipped');
-- Both are valid documents. A reader that assumes "status" exists is
-- fine until the day it does not, and the error surfaces in the
-- application rather than at the write. The relational version cannot
-- get into that state: status is NOT NULL in the table definition, and
-- the database refuses the row.
--
-- Neither model is the correct one. They put the same complexity in
-- different places -- schema up front, or handling in every reader.
Result
Try it yourself
Step through all five models and watch the "joins needed" counter. Relational needs two joins for Ada's orders; the document model needs none.
Look at the document view closely. The customer's city is repeated inside every order — that duplication is what buys the single-read speed.
Try the key–value model and read its trade-off note. There is no way to ask "which customers live in London" without scanning everything.
Add the field in relational, then in document. One is a table-wide migration; the other touches a single record.
Worth remembering
There is no "better" model, only different bargains. Relational gives you enforced consistency and flexible ad-hoc queries; non-relational gives you speed, scale and schema freedom in exchange for moving integrity into your application. Most real systems today use both.
Consistency, CAP and what "eventual" means
Distributed stores face a constraint that a single-node database does not. The CAP theorem says that when the network between nodes fails, a system must choose between staying consistent (refuse to answer rather than return stale data) and staying available (answer with whatever this node knows).
Many NoSQL systems chose availability, which produces eventual consistency: a write is acknowledged immediately, replicas catch up shortly afterwards, and for a brief window different clients can read different values. For a like counter or a viewed-products list, that is entirely fine. For an account balance or a seat booking, it is not.
Most modern systems make this tunable per operation rather than fixing it for the whole database — write to a quorum for the operations that matter, write to one node for the ones that do not. DynamoDB, Cassandra and MongoDB all expose some version of this, and choosing it deliberately is part of using them well.
The related trade is transactions. Single-document atomicity is universal; multi-document transactions are now available in several document stores but cost more than their relational equivalents and are not the default idiom.
Choosing between them
Reach for a non-relational store when:
The access pattern is known, narrow and dominated by one key — "get everything for this user id".
Write throughput exceeds what one node can serve, and the data partitions cleanly.
The records are genuinely heterogeneous, such as event payloads from many sources.
The workload is relationship traversal many hops deep, where a graph database is orders of magnitude faster than recursive joins.
You need a cache, a queue or a session store — which is a key-value job, not a database design question.
Stay relational when queries will be ad hoc, when several records must change atomically, when the data has clear structure and referential rules, or when reporting matters — SQL is still the best query language anyone has built for questions you did not anticipate.
Polyglot persistence — PostgreSQL for the core data, Redis for the cache, Elasticsearch for search — is common and sensible. The cost is operational: several systems to run, back up and keep consistent with each other.
Questions people ask
Is NoSQL faster? For its designed access pattern, often yes. For an unanticipated query, usually much slower — there may be no index and no join to fall back on.
Can I do joins? Document stores offer limited lookups (MongoDB's $lookup), and they are not the idiom. If you find yourself joining constantly, the data model or the database choice is wrong.
Does NoSQL mean no schema? No — it means the schema is not enforced by the database. It still exists, in your code.
Should I start with NoSQL for a new project? Rarely. Early projects change their query patterns constantly, which is exactly the situation relational databases handle best. Adopt a specialised store when a specific need appears.
What is NewSQL? Distributed systems (CockroachDB, Spanner, TiDB) that keep SQL and ACID guarantees while scaling horizontally — an attempt to stop the choice being necessary.
Can PostgreSQL just do this? Often, yes. JSONB covers many document use cases with indexing and constraints available, and it means one system to operate rather than two.
Recap in one screen
"NoSQL" covers four different families: document, key-value, wide-column and graph.
Each optimises one access pattern — and is poor at the ones it was not designed for.
Flexible schemas move the schema into your code; they do not remove it.
Denormalisation buys single-read speed and hands you the consistency problem.
Many systems trade immediate consistency for availability; make that choice per operation, deliberately.
Predict, then reveal
About to run: press Add field to one record. Before it does — what happens to the readout?
Committing to an answer first is the point — the reveal runs the experiment on the visualisation above and reads the real value back, so nothing here is scripted.
Recall check
0 of 3
Say the answer out loud before you reveal it — recalling it is what makes it stick, and rereading it is not.
Without scrolling back — what is the one-line takeaway from this module?
There is no "better" model, only different bargains. Relational gives you enforced consistency and flexible ad-hoc queries; non-relational gives you speed, scale and schema freedom in exchange for moving integrity into your application. Most real systems today use both.
What does this module say about “Quick Context”?
A non-relational database stores data in a shape other than fixed tables of rows and columns. The name "NoSQL" is a historical accident — it is best read as "not only SQL" , since many of these systems now support SQL-like query languages.
What does this module say about “The Four Families”?
The headline benefit of a document store is that you can add a field to one document without altering anything else. For rapidly changing data, or genuinely heterogeneous records, that removes real friction: no migration, no downtime, no coordination.
Cheat sheet
What are Non Relational Databases?
The same data, stored four different ways. Compare documents, key-value pairs, wide columns and graphs against the relational original — and see what each model makes easy and what it makes painful.
Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.