SQL vs NoSQL Databases: A First-Principles Decision Framework
Compare relational and non-relational databases from first principles. Learn ACID properties, NoSQL types, polyglot persistence, and when to pick each — with real migration stories from Uber and Netflix.
What you'll learn
- ✓Understand the core differences between relational and non-relational databases
- ✓Explain ACID properties with concrete transaction examples
- ✓Identify the four major NoSQL families and their sweet spots
- ✓Apply a decision framework for choosing SQL vs NoSQL
- ✓Design polyglot persistence architectures that use multiple databases
Prerequisites
- •Basic understanding of what a database does
- •Familiarity with simple SQL queries is helpful but not required
Choosing between SQL and NoSQL is not about picking the “better” technology. It is about understanding your data shape, your access patterns, and your consistency requirements — then matching them to the database engine that handles those constraints most naturally. This article walks through that decision from first principles.
The Relational Model: Where It All Started
In 1970, Edgar Codd published a paper proposing that data should be organized into tables (relations) with rows and columns, and that a declarative language should let you query those tables without worrying about how the data is physically stored. That idea became the relational database.
Think of a relational database like a set of interconnected spreadsheets. Each spreadsheet (table) has strict column definitions, and you can link spreadsheets together using shared keys. The crucial property is that every row in a table follows the same schema — no surprises.
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
total DECIMAL(10,2) NOT NULL,
status VARCHAR(20) DEFAULT 'pending'
);
This rigid structure is not a limitation — it is a feature. When your data has clear relationships and you need to query it from many angles, schemas enforce correctness at the database level instead of hoping your application code gets it right.
ACID: The Guarantees That Make Banks Sleep at Night
Relational databases earned enterprise trust through ACID properties. Let us make these concrete with a bank transfer example: moving $500 from Alice’s account to Bob’s.
Atomicity means the transfer either fully completes or fully rolls back. You never end up in a state where $500 left Alice’s account but did not arrive in Bob’s. It is all-or-nothing, like a light switch — on or off, never halfway.
Consistency means the database moves from one valid state to another. If there is a rule that account balances cannot go negative, the database rejects the entire transaction rather than allowing an invalid state.
Isolation means concurrent transactions do not interfere with each other. If Alice is transferring money to Bob while Carol is transferring money to Bob at the same time, each transaction sees a consistent view of the data, as if they ran one after another.
Durability means once the transfer is confirmed, it survives even if the server crashes a millisecond later. The data is written to stable storage before the “success” response goes back to the client.
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 500
WHERE user_id = 'alice' AND balance >= 500;
UPDATE accounts SET balance = balance + 500
WHERE user_id = 'bob';
COMMIT;
If anything fails between BEGIN and COMMIT, the entire transaction rolls back. This is the kind of guarantee that financial systems, healthcare platforms, and booking engines depend on.
The Non-Relational Revolution: Why NoSQL Emerged
By the mid-2000s, companies like Google, Amazon, and Facebook were hitting walls that relational databases were not designed for. The pain points were specific:
- Schema rigidity made it hard to iterate quickly on product features.
- Horizontal scaling was difficult because relational databases were designed to run on a single powerful machine.
- Data shapes did not always fit neatly into tables. A social media post with nested comments, reactions, and variable metadata maps awkwardly to a normalized relational schema.
NoSQL is not one thing — it is an umbrella term for “anything that is not a traditional relational database.” The four major families each solve different problems.
The Four NoSQL Families
Document Stores (MongoDB, CouchDB)
Document databases store data as flexible JSON-like documents. Each document can have a different structure, which maps naturally to how applications think about data.
{
"_id": "user_12345",
"name": "Alice Chen",
"email": "alice@example.com",
"addresses": [
{ "type": "home", "city": "San Francisco", "zip": "94102" },
{ "type": "work", "city": "Palo Alto", "zip": "94301" }
],
"preferences": {
"theme": "dark",
"notifications": true
}
}
The sweet spot: content management systems, user profiles, product catalogs — anywhere the data structure varies between records or contains nested objects. You trade join capabilities for flexible schemas and natural data locality (all related data in one document instead of spread across five tables).
Key-Value Stores (Redis, DynamoDB)
Key-value stores are the simplest NoSQL model. You store a value (string, JSON, binary blob) under a unique key, and you retrieve it by that key. Think of it as a giant, distributed hash map.
# Redis examples
redis.set("session:abc123", json.dumps(session_data), ex=3600)
session = json.loads(redis.get("session:abc123"))
redis.incr("page_views:homepage")
redis.lpush("recent_orders", order_id)
The sweet spot: session storage, caching, real-time leaderboards, rate limiting — anywhere you need sub-millisecond lookups by a known key. You sacrifice query flexibility for raw speed.
Column-Family Stores (Cassandra, HBase)
Column-family databases organize data into rows and column families, but unlike relational databases, each row can have different columns. They are designed from the ground up for massive write throughput and horizontal scaling across many nodes.
CREATE TABLE user_activity (
user_id UUID,
timestamp TIMESTAMP,
event_type TEXT,
metadata MAP<TEXT, TEXT>,
PRIMARY KEY (user_id, timestamp)
) WITH CLUSTERING ORDER BY (timestamp DESC);
The sweet spot: time-series data, event logging, IoT sensor data — anywhere you have massive write volumes with predictable query patterns. Netflix uses Cassandra to handle trillions of rows of viewing history because it scales linearly by adding nodes.
Graph Databases (Neo4j, Amazon Neptune)
Graph databases model data as nodes connected by edges (relationships). When your queries are fundamentally about traversing connections — “find friends of friends who also like jazz” — a graph database can answer in milliseconds what would require expensive recursive joins in SQL.
// Find mutual friends who live in the same city
MATCH (me:Person {name: "Alice"})-[:FRIENDS_WITH]->(friend)
-[:FRIENDS_WITH]->(fof:Person)
WHERE fof.city = me.city AND fof <> me
RETURN fof.name, COUNT(friend) AS mutual_friends
ORDER BY mutual_friends DESC
The sweet spot: social networks, recommendation engines, fraud detection, knowledge graphs — anywhere relationships between entities are as important as the entities themselves.
The Decision Framework
Choosing between SQL and NoSQL is not a religious debate. Here is a practical framework:
Choose SQL when:
- Your data has clear relationships and you need complex joins
- You require strong ACID transactions (financial systems, inventory)
- Your schema is relatively stable and well-understood
- You need ad-hoc queries from many different angles
- Your dataset fits on a single machine or a small cluster
Choose NoSQL when:
- Your data structure varies between records
- You need horizontal write scaling across many nodes
- Your access patterns are well-defined and key-based
- You are dealing with massive volumes of semi-structured data
- Development speed matters more than query flexibility
The real question is not “SQL or NoSQL?” but rather “What are my access patterns, consistency requirements, and scaling constraints?”
Polyglot Persistence: Using Multiple Databases
Modern systems rarely use a single database. Polyglot persistence means picking the right database for each workload within the same system.
Consider an e-commerce platform:
- PostgreSQL for the product catalog, orders, and inventory (needs ACID transactions and complex queries)
- Redis for session storage, shopping cart, and caching (needs sub-millisecond reads)
- Elasticsearch for product search and filtering (needs full-text search with facets)
- Cassandra for clickstream analytics and user activity logs (needs massive write throughput)
┌─────────────────────────────────────────────────┐
│ Application Layer │
├──────────┬──────────┬───────────┬───────────────┤
│ Products │ Sessions │ Search │ Analytics │
│ Orders │ Cart │ Filters │ Click logs │
│ Users │ Cache │ │ Events │
├──────────┼──────────┼───────────┼───────────────┤
│PostgreSQL│ Redis │Elastic │ Cassandra │
│ (ACID) │ (Speed) │search │ (Scale) │
└──────────┴──────────┴───────────┴───────────────┘
The trade-off is operational complexity. Each database is another system to deploy, monitor, back up, and keep in sync. Data consistency across multiple stores requires careful application-level coordination.
Real-World Migration Stories
Uber: Postgres to MySQL
Uber famously migrated from PostgreSQL to MySQL — not because MySQL is “better,” but because of specific operational issues they hit at scale. Their main pain points were:
- PostgreSQL’s write amplification during updates (it creates a new tuple for every update due to MVCC)
- Replication architecture that required the replica to keep up with the primary’s write-ahead log
- Difficulty with connection pooling at their scale
They moved to MySQL with InnoDB because its update-in-place storage engine and mature replication tooling better matched their operational needs. The lesson: database choice at scale is about operational characteristics, not feature checklists.
Netflix: Cassandra at Scale
Netflix chose Cassandra for their primary data store because they needed:
- Multi-datacenter replication with no single point of failure
- Linear horizontal scaling (just add nodes)
- Tunable consistency per query (some reads can be eventually consistent, others need quorum)
They run one of the largest Cassandra deployments in the world, handling trillions of requests per day across thousands of nodes. Their viewing history, which is write-heavy and rarely updated, maps perfectly to Cassandra’s strengths.
Common Misconceptions
“NoSQL means no schema.” Every database has a schema — sometimes it is enforced by the database, sometimes by your application code. Moving schema enforcement to the application does not eliminate it; it just moves the responsibility.
“SQL does not scale.” Companies like Shopify, GitHub, and Figma run massive PostgreSQL and MySQL deployments. With read replicas, connection pooling, and careful partitioning, relational databases scale much further than people assume.
“NoSQL is always faster.” A well-indexed SQL query can be faster than a poorly designed NoSQL query. Performance depends on data modeling, indexing, and access patterns — not the database category.
“You should pick one and standardize.” Polyglot persistence exists because different workloads have genuinely different requirements. Standardizing on one database for everything means at least some workloads will be awkwardly shoe-horned.
Wrapping Up
SQL and NoSQL are tools, not ideologies. Relational databases give you schema enforcement, powerful joins, and ACID transactions — invaluable when data integrity is paramount. NoSQL databases give you flexible schemas, horizontal scaling, and data models that match specific access patterns.
The best architects do not pick a side. They understand their data, their access patterns, and their consistency requirements, then choose the database — or combination of databases — that fits. Start with the simplest option that works, and evolve as your understanding of the workload deepens.
Related articles
- AWS AWS DynamoDB Data Modeling Patterns
Practical DynamoDB modeling patterns including single-table design, composite keys, GSIs, and access-pattern-first thinking that keeps queries cheap at scale.
- SQL SQL NULL Handling Best Practices
Learn how NULL behaves in SQL, why three-valued logic trips up queries, and the patterns that keep your data consistent and your queries correct.
- System Design Database Replication: Leaders, Followers, and Consistency
Master database replication patterns — single-leader, multi-leader, and leaderless. Learn about replication lag, conflict resolution, quorum reads, and how Slack handles replication at scale.
- System Design Database Transactions and ACID: Isolation Levels Demystified
Deep dive into ACID properties, isolation levels, and distributed transactions. Understand dirty reads, phantom reads, two-phase commit, the saga pattern, and how Stripe handles payment consistency.