Part 4 · Databases & Data at Scale

SQL vs NoSQL: Picking the Right Data Model

“SQL or NoSQL?” is the wrong question; the right one is “what shape is my data, and how will I read and write it?” The relational model and the four NoSQL families — key-value, document, wide-column and graph — what each is good at, the myths to drop, and a sensible default.

Lesson 2012 min readintermediate#database#nosql

On this page

  1. The Problem
  2. The Core Idea
  3. How It Works
  4. Trade-offs
  5. In the Real World
  6. Interview Questions
  7. Key Takeaways
  8. Further Reading

The Problem

You are starting a new project and someone asks, "SQL or NoSQL?" SQL databases store data in tables and are queried with the SQL language. "NoSQL" is a loose name for all other kinds of databases (key-value, document, wide-column and graph). Here is what people say:

  • One teammate says "NoSQL scales better, let's use MongoDB."
  • Another says "Just use Postgres for everything."
  • A blog post says Cassandra is what Netflix uses, so it must be good.

This choice matters, because changing your database later is one of the hardest migrations in software. A migration means moving all data and code to something new. But "SQL vs NoSQL" is really the wrong question. The right question is: what shape is my data, and how will I read and write it?

The Core Idea

Think about different ways to keep papers and information at home:

  • A filing cabinet with labelled forms (relational/SQL). Every form has the same fields. A customer form points to an order form by its number. It is very organised. It is easy to cross-check ("show me every order for this customer").
  • A folder per topic, containing whatever notes you need (document). Everything about one trip (tickets, hotel, plans) is in one folder. It is flexible. One folder can look different from another.
  • Numbered lockers (key-value). Locker 4521 holds something. You cannot search by what is inside. You just open the locker by its number. It is extremely fast.
  • A huge spreadsheet, split into parts that sit in many rooms (wide-column). Built for a massive amount of data that is written very quickly.
  • A wall of photos connected by strings (graph). Perfect for "who knows whom" and "friends of friends".

Each is great for some jobs and awkward for others. NoSQL is not "better SQL". It is a different set of trade-offs (you gain something and you give up something).

How It Works

Relational databases (SQL)

A relational database keeps data in tables (rows and columns). The table has a fixed schema. A schema is the plan of the data: the column names and the type of each column. Tables are linked by keys. A key is a column value that identifies a row or points to a row in another table (see Lesson 5). Here, orders.user_id points to users.id.

Diagram
users                         orders
┌────┬───────┐                ┌────┬─────────┬────────┐
│ id │ name  │                │ id │ user_id │ total  │
├────┼───────┤                ├────┼─────────┼────────┤
│ 42 │ Asha  │◄───────────────│ 7  │ 42      │ 1,299  │
└────┴───────┘                └────┴─────────┴────────┘

Strengths:

  • Joins: a join combines rows from two tables in one query. You can ask questions you did not plan for.
  • Transactions (ACID): a transaction is a group of changes that either all succeed or all fail. ACID is the set of safety promises for transactions (see Lesson 21).
  • Constraints: rules that the database enforces for you. Examples: emails must be unique, and a foreign key (a column that points to a row in another table) must point to a row that exists.
  • SQL: a powerful, standard language for asking questions of the data. Most developers know it.
  • Mature tools: backups, monitoring, ORMs (libraries that let you use tables as objects in your code) and decades of experience.

Weaknesses:

  • Schema changes on huge tables need care.
  • Scaling writes beyond one machine is hard. You must split the data across machines (sharding, Lesson 25).
  • Joins across many machines are slow and costly.

Examples: PostgreSQL, MySQL, SQL Server, Oracle, SQLite.

Key-value stores

This is the simplest model. A key maps to a value, like a word in a dictionary. The database does not look inside the value.

HTTP
"session:abc123"  →  {"userId": 42, "expires": "..."}"cart:42"         →  [991, 405, 17]"rate:ip:1.2.3.4" →  87
  • ✅ Extremely fast and easy to scale (you just spread the keys across machines).
  • ❌ You can only look things up by key. "Find all carts that contain product 991" is not possible without extra work.

Examples: Redis, Amazon DynamoDB (key-value plus some document features), Memcached, etcd. Good for: sessions (login state), caches, shopping carts, feature flags (on/off switches for features), counters.

Document databases

Each record is a document. A document is usually JSON-like and can contain nested data (objects inside objects). A collection is a group of documents, like a table. Documents in the same collection do not need the same fields.

JavaScript
{  "_id": "order_7",  "user": { "id": 42, "name": "Asha" },  "items": [    { "productId": 991, "name": "Running shoes", "qty": 1, "price": 1299 }  ],  "status": "shipped",  "shippingAddress": { "city": "Pune", "pin": "411001" }}
  • ✅ Data that is read together is stored together. One read fetches the whole order, with no joins.
  • ✅ A flexible schema is easy to change over time, and it fits JSON APIs well.
  • ❌ Weaker at joins and at relationships between documents.
  • ❌ Copied data (like the user's name inside every order) must be kept in sync. If the name changes, you must update every copy.
  • ❌ "Schemaless" really means the schema lives in your code. Inconsistent data can slowly get into the database.

Examples: MongoDB, Couchbase, Firestore, Amazon DocumentDB. Good for: product catalogues (different products have different attributes), content management (CMS), user profiles, event data.

Wide-column databases

These look like tables, but they are built to be spread across many machines and to handle a huge number of writes. Two keys organise the data. The partition key decides which machine holds a row. The clustering key decides the order of rows inside that partition. All rows with the same partition key stay together.

Diagram
Partition key: channel_id     Clustering key: message_time (newest first)
┌─────────────┬─────────────────────┬──────────┬──────────────┐
│ channel_id  │ message_time        │ user     │ text         │
├─────────────┼─────────────────────┼──────────┼──────────────┤
│ general     │ 2026-12-07 10:02:11 │ asha     │ "hi all"     │
│ general     │ 2026-12-07 10:01:55 │ ravi     │ "morning!"   │
└─────────────┴─────────────────────┴──────────┴──────────────┘
  • ✅ Massive scale and very fast writes. In Cassandra's design there is no single master (no one main node that all writes must go through).
  • ✅ Great for data in time order for each key: messages per channel, readings per sensor, events per user.
  • ❌ You must design tables around your queries. Queries that do not use the partition key are slow or not allowed.
  • ❌ No joins, and limited transactions.

Examples: Apache Cassandra, ScyllaDB, HBase, Google Bigtable. Good for: chat messages, IoT (internet-connected device) sensor data, activity feeds, logs at huge scale.

Graph databases

A graph database stores data as nodes (things, such as people) and edges (the links between them, such as "follows"). Following links is fast, even many steps deep.

A tiny social graph
(Asha) ──FOLLOWS──► (Ravi) ──FOLLOWS──► (Meera)
   │                                       ▲
   └──────────LIKES──► (Post 17) ◄─WROTE───┘

Take this question: "people Asha might know: friends of her friends whom she does not follow yet". It is simple in a graph database. In SQL it needs many self-joins (joining a table to itself again and again), which is painful.

Examples: Neo4j, Amazon Neptune. Good for: social networks, recommendation engines, fraud detection (finding groups of linked accounts), knowledge graphs.

Relational
  • +Joins, ACID, constraints, flexible queries
  • −Scaling writes across machines takes work
Use whenMost business apps (PostgreSQL, MySQL)
Key-value
  • +Fastest lookups; trivial to scale out
  • −Only by key
Use whenSessions, caches, carts (Redis, DynamoDB)
Document
  • +Read the whole record in one go; flexible schema
  • −Weak across documents; duplicated data to sync
Use whenCatalogues, CMS, profiles (MongoDB)
Wide-column
  • +Massive write scale; time-ordered per key
  • −Tables designed per query; no joins
Use whenMessages, IoT, events (Cassandra, ScyllaDB)
Graph
  • +Deep relationship queries are easy and fast
  • −Niche tool; smaller ecosystem
Use whenSocial, fraud, recommendations (Neo4j)
The five data models side by side. · compare

Other specialised databases

  • Time-series (TimescaleDB, InfluxDB, Prometheus): built for values that change over time, such as metrics and sensor data. They compress data well and answer time-based queries fast.
  • Search engines (Elasticsearch, OpenSearch): full-text search (Lesson 19).
  • Analytics / columnar (ClickHouse, BigQuery, Snowflake): they store data by column. This makes huge aggregations (sums, averages, counts) over billions of rows fast.
  • Vector databases: find items that are similar in meaning by comparing embeddings (lists of numbers made by a machine-learning model). See Lesson 19.

Schema-on-write vs schema-on-read

  • Schema-on-write (SQL): the database checks the data when you write it. Wrong types or missing fields are rejected.
  • Schema-on-read (most NoSQL): anything can be written. Your code checks and understands the data when it reads it. It must handle old and unusual formats.

Neither one means "no schema". The real question is who enforces the schema: the database or your application.

Two ways to design your data

Entity-first (typical SQL)
  • •Model users, orders, products and relationships, normalised
  • •Then write whatever queries you need
  • +Flexible for questions nobody has asked yet
Access-pattern-first (typical NoSQL)
  • •List the exact queries first
  • •Shape storage so each is one fast lookup
  • +Very fast at scale
  • −New kinds of query may need new tables or copies
Two ways to start a data model. · compare

Entity-first (typical SQL). Model the real-world things (users, orders, products) and their relationships. Keep the data normalised (each fact stored once; see Lesson 23). Then write whatever queries you need. It is flexible for questions nobody has asked yet.

Access-pattern-first (typical NoSQL). First, list the exact queries ("get order by ID", "get a user's last 20 orders", "get messages in a channel, newest first"). An access pattern is one such way of reading data. Then design the storage so each query is a single, fast lookup. It is very fast at scale. But new kinds of queries may need new tables or extra copies of data.

With DynamoDB, some teams go as far as "single-table design". They keep many kinds of items (users, orders and so on) in one table. They choose the keys with care, so related items are stored next to each other.

Myths to drop

  • "NoSQL scales, SQL doesn't." Relational databases handle very large workloads on one powerful machine plus read replicas (extra copies that serve reads). They can also be sharded (Lesson 25). Many huge companies run on MySQL or PostgreSQL.
  • "NoSQL is schemaless, so it is faster to build with." It is faster at the start. Later, inconsistent data can slow you down.
  • "SQL can't do JSON." PostgreSQL's JSONB type stores JSON in a binary form that can be indexed. It works very well for JSON documents. So you can mix relational and document styles in one database.
  • "You must choose one." Many systems use several databases, each for the job it does best. This is called polyglot persistence (polyglot means "many languages").

NewSQL / distributed SQL

A newer group of databases wants SQL + transactions + horizontal scale (adding more machines). They split the data across machines for you. Still, they look like one SQL database to your code.

Examples: Google Spanner, CockroachDB, YugabyteDB, TiDB.

The trade-offs: they are harder to run. Some writes are slower (latency is higher), because machines must agree with each other first. They can also cost more.

A quick comparison

RelationalKey-valueDocumentWide-columnGraph
Data shapeTables, fixed columnsKey → blobNested JSON docsRows by partition keyNodes + edges
Flexible queries✅ Excellent❌ Key only🟡 Good within a doc❌ Must match the design✅ For relationships
Joins✅❌🟡 Limited❌✅ (traversals)
Transactions✅ Strong🟡 Limited🟡 Improving🟡 Limited🟡 Varies
Horizontal write scale🟡 Harder✅✅✅ Excellent🟡 Harder
Typical useMost business appsCache, sessionsCatalogues, CMSMessages, IoT, eventsSocial, fraud, recs

Trade-offs

  • Relational gives you correct data, flexible queries and mature tools. But scaling writes across machines takes real work.
  • Document gives you speed when you "read the whole thing" and a flexible schema. But relationships and consistent data become your job.
  • Wide-column gives you massive write scale. But you must know your queries in advance and accept limited transactions.
  • Key-value is the simplest and fastest. But it only works by key.
  • Graph makes deep relationship queries easy. But it is a niche tool with a smaller ecosystem.
  • Many databases means the best tool for each job. But you have more systems to learn, run, back up and keep in sync.

A sensible default for most new products: start with a relational database (PostgreSQL or MySQL). Add Redis for caching. Add a specialised database only when a clear need appears, such as huge write volumes, graph queries or full-text search.

In the Real World

Discord's messages. Discord started by storing messages in MongoDB. As it grew to billions of messages, it moved to Cassandra (Discord described this in a 2017 blog post). Its data (messages per channel, ordered by time) fits the wide-column model very well. Years later, it had trillions of messages. It moved again to ScyllaDB, a Cassandra-compatible database, for better performance and easier operation (described in 2023). The data model stayed the same. Only the engine changed.

Amazon's shopping cart and Dynamo. Amazon's 2007 Dynamo paper described a key-value store built so that the shopping cart would always accept writes, even during failures. A cart that refuses "add to cart" loses money. Those ideas inspired Cassandra and Riak. They also shaped Amazon DynamoDB, which is a separate, later service.

Instagram, Shopify and GitHub on relational databases. All three reached massive scale on PostgreSQL or MySQL, adding caching, replicas and sharding along the way. They show that "SQL does not scale" is not true.

LinkedIn and graphs. Features like "people you may know" and "2nd-degree connections" are natural graph problems. LinkedIn built its own graph systems to answer such questions quickly.

Polyglot persistence at a typical e-commerce company:

  • PostgreSQL for orders and payments,
  • Redis for carts and sessions,
  • Elasticsearch for product search,
  • a columnar warehouse for analytics,
  • object storage for images.

Interview Questions

Q1When would you choose a NoSQL database over a relational one?

Choose it when the data and the access patterns fit a NoSQL model better. Examples: a very high write volume with a known access pattern (wide-column); simple lookups by key at huge scale (key-value); self-contained records with different fields (document); or following relationships many steps deep (graph). You should also not need joins or multi-record transactions across that data.

Q2What does “schemaless” really mean?

The database does not enforce a schema when you write. The schema still exists, but it lives in the application code. That code must handle every shape of data that was ever written. It is schema-on-read instead of schema-on-write.

Q3How do you model data for Cassandra or DynamoDB?

Start from the queries, not from the entities. Choose a partition key that spreads the load evenly and keeps together what one query needs. Add a clustering key (called a sort key in DynamoDB) to set the order inside the partition. Build one table or index for each access pattern. Accept that some data will be duplicated.

Q4Is it true that SQL doesn't scale?

No. One powerful relational server with read replicas handles very large workloads. Relational databases can also be sharded. Instagram, Shopify and GitHub run on PostgreSQL or MySQL. The harder part is scaling writes across many machines automatically.

Q5Can PostgreSQL replace a document database?

Often, yes. JSONB columns with GIN indexes (an index type that is good for searching inside JSON) can store and query flexible documents. You keep SQL, joins, constraints and transactions for the rest of your data. A dedicated document database makes more sense at very large scale, or when built-in sharding is the main need.

Q6What is distributed SQL, and what does it cost?

These are databases like Spanner, CockroachDB, YugabyteDB and TiDB. They shard and replicate data automatically and still give you SQL and ACID transactions. They use consensus (a way for machines to agree on one answer). The cost is higher write latency, because a commit needs a quorum (a majority of the machines to agree), and this is slower across regions. They are also harder to operate.

Key Takeaways

  • The real question is not "SQL or NoSQL?". It is what shape your data is and how you will read and write it.
  • Relational is best for structured data, joins, transactions and flexible queries. It is a strong default.
  • The NoSQL families each have their strengths:
    • key-value for simple fast lookups,
    • document for self-contained records,
    • wide-column for huge write-heavy, time-ordered data,
    • graph for relationships.
  • NoSQL usually means designing around your access patterns and enforcing the schema in code.
  • Mixing databases (polyglot persistence) is normal. Distributed SQL gives you SQL with horizontal scale, but at a cost.

Further Reading

  • Designing Data-Intensive Applications by Martin Kleppmann (chapter 2, "Data Models and Query Languages")
  • The Amazon Dynamo paper (SOSP 2007) and the Google Bigtable paper (OSDI 2006)
  • The Cassandra paper, "Cassandra: A Decentralized Structured Storage System" (LADIS 2009)
  • Discord's engineering blog posts on storing billions, and later trillions, of messages
  • The System Design Primer on GitHub (sections "NoSQL" and "SQL or NoSQL")

Published by Jainik · Last updated · How these lessons are made

Keep learning

Part of the study guide

Related series