When would you choose NoSQL Database over SQL?
A ground-up, no-jargon walkthrough of relational and non-relational databases — how they work internally, why NoSQL was invented, and exactly how to decide which one your project actually needs.
Introduction & History
A tidy notebook, a chaotic warehouse, and 50 years of engineering that split databases into two very different families.
Imagine you own a small toy shop. At first, you keep track of every toy, every price, and every customer order in one big notebook, with neat rows and columns. One page has a table for toys, another has a table for customers, and a third connects the two — “Customer #12 bought Toy #45 on Tuesday.” This notebook is tidy, predictable, and easy to check for mistakes. This is exactly how a relational database (SQL) behaves.
Now imagine your toy shop becomes a giant online marketplace overnight. Millions of people from every country browse toys at the same second. Some toys have wildly different details — one is a wooden block with just a colour and size, another is a smart robot with fifty configurable settings. Your one neat notebook can no longer be copied fast enough, shared fast enough, or reshaped fast enough for every kind of toy. You need something more flexible and something that can be split across thousands of shelves in thousands of warehouses, all around the world, while still working smoothly. This is the exact situation that gave birth to NoSQL databases.
Analogy. A SQL database is like a library with a strict card catalog: every book must be entered in the same format, on the same size card, filed in the same drawer system. A NoSQL database is like a giant warehouse of storage bins: you can throw in a shoebox, a filing folder, or a rolled-up poster, and the warehouse simply labels the bin and remembers where it is — no card catalog required.
1.1 A short history
In the 1970s, Dr. Edgar F. Codd at IBM proposed the relational model — organising data into tables of rows and columns, linked by shared keys. This idea became SQL (Structured Query Language) and powered nearly every business system for the next 30 years: banks, airlines, hospitals, and government records all relied on it because it guaranteed correctness above everything else.
By the early 2000s, companies like Google, Amazon, and later Facebook were facing a new kind of problem: not “is our data correct?” but “can our data survive being read and written by a billion people at once, spread across continents?” Traditional relational databases, built for a single powerful machine, struggled to spread themselves across hundreds of cheap machines without slowing down or breaking consistency rules.
2004 · Bigtable
Google publishes the Bigtable paper, describing a distributed storage system for structured data.
2007 · Dynamo
Amazon publishes the Dynamo paper, describing a highly available key-value store designed to keep the shopping cart working during massive traffic and partial failures.
2009 · “NoSQL” goes mainstream
The term “NoSQL” is popularised at a meetup in San Francisco, and databases like MongoDB, Cassandra, and CouchDB gain mainstream traction.
2010s – today · managed & polyglot
Cloud providers (AWS DynamoDB, Google Firestore, Azure Cosmos DB) turn NoSQL into a fully managed, pay-as-you-go service, and “NoSQL” is reinterpreted to mean “Not Only SQL” — a toolbox to be combined with relational databases, not a total replacement.
The Problem & Motivation
Three pressures — shape, size, and speed — that broke the old assumption of “one powerful database on one big machine.”
To understand why NoSQL exists, you first need to understand what started breaking as applications grew. Three pressures pushed engineers to look beyond traditional SQL databases.
Pressure 1: The data no longer fits in neat rows
SQL databases expect a fixed schema — a blueprint that says “every row in the ‘products’ table must have exactly these columns, in this order, with these data types.” But modern applications deal with messy, changing, and nested information: a social media post might have text, or an image, or a video, or a poll, or all four. Forcing all of that into fixed columns becomes painful and wastes storage with lots of empty (NULL) fields.
Pressure 2: One computer is not enough
A traditional SQL database usually runs on one powerful machine (this is called vertical scaling — buying a bigger, stronger computer). But there is a limit to how big a single computer can get, and it becomes extremely expensive. NoSQL databases were designed from day one to spread data across many ordinary machines — this is called horizontal scaling — which is cheaper and has no real ceiling.
Pressure 3: The world does not wait for a single “correct” answer
In a bank, if you check your balance, you need the exact right number — right now. But if you’re looking at how many people liked a photo on Instagram, it genuinely does not matter if the count is one second out of date. Relational databases are built to always give the exact right, up-to-the-millisecond answer (a property called strong consistency), even if that means slowing down. Many NoSQL databases instead choose to answer instantly, even if the answer might be a fraction of a second stale (called eventual consistency) — because for most modern apps, speed and availability matter more than perfect real-time accuracy.
Core Concepts You Must Know
A tight vocabulary before we go deeper — every term in the rest of the guide is defined here, one at a time.
3.1 What is SQL (a relational database)?
A relational database stores data in tables (like spreadsheets), where each table has fixed columns (name, age, email) and each row is one record. Tables are linked to each other using keys — a primary key uniquely identifies a row, and a foreign key in one table points to the primary key of another table, creating a “relationship.” You interact with this data using SQL, a language for asking precise questions like “give me every customer who bought a red toy last month.”
customers table has columns id, name, email. An orders table has columns id, customer_id, product, price, where customer_id is a foreign key pointing back to customers.id. This link is what lets you ask, “show me all orders placed by customer 12.”
3.2 What is NoSQL (a non-relational database)?
NoSQL is an umbrella term for databases that do not force data into rigid tables with fixed columns. Instead, they store data in more flexible shapes — documents, key-value pairs, wide columns, or graphs (explained in detail in Section 4). Most NoSQL databases favour being fast and always-available at huge scale, sometimes trading away some of the strict correctness guarantees that SQL databases hold sacred.
3.3 ACID — the promise SQL databases make
ACID is a set of four guarantees that most relational databases promise for every transaction (a “transaction” is a group of operations that must all succeed or all fail together, like “move ₹500 from Account A to Account B”).
| Letter | Stands for | Plain-English meaning |
|---|---|---|
| A | Atomicity | All steps in a transaction happen, or none do. Money can’t leave Account A without arriving in Account B. |
| C | Consistency | The database always moves from one valid state to another — no broken rules, ever. |
| I | Isolation | Two transactions happening at the same time don’t mess each other up, as if they happened one after another. |
| D | Durability | Once a transaction is confirmed, it survives even a power cut or crash — it’s permanently saved. |
Analogy. ACID is like a strict bank teller who will never hand you cash unless the matching amount is deducted from your account in the exact same breath, and who writes every transaction into a permanent ledger in ink, not pencil.
3.4 BASE — the promise many NoSQL databases make instead
Many NoSQL systems follow a looser set of guarantees called BASE:
- Basically Available — the system always responds, even during failures, rather than refusing to answer.
- Soft state — the data might change over time even without new input, as the system quietly syncs copies in the background.
- Eventual consistency — if no new updates come in, all copies of the data will eventually match up — just maybe not instantly.
Analogy. BASE is like a group chat rumour. When you tell your friend some news, they know it instantly, but it takes a few minutes for the news to reach everyone else in the group. Eventually, everyone knows the same thing — just not at the exact same second.
3.5 The CAP theorem
The CAP theorem, proven by computer scientist Eric Brewer, states that any distributed database (one spread across multiple machines) can only fully guarantee two out of these three properties at the same time:
- Consistency (C) — every read gets the most recent write, or an error.
- Availability (A) — every request gets a (non-error) response, even if it’s not the very latest data.
- Partition tolerance (P) — the system keeps working even if the network between machines breaks (a “network partition”).
Because real-world networks do fail sometimes, partition tolerance is not optional for any distributed system — so in practice, every distributed database must choose between prioritising Consistency or Availability when a network partition happens.
3.6 Schema-on-write vs. schema-on-read
SQL databases use schema-on-write: you must define the exact structure (columns and types) before you insert a single row. NoSQL databases typically use schema-on-read: you can store data in whatever shape you like, and your application decides how to interpret it when reading it back. This gives huge flexibility but shifts the responsibility of “keeping data clean” from the database onto your application code.
Types of NoSQL Databases
“NoSQL” is not one single technology — it’s a family of four major categories, each shaped for a different kind of problem.
Key-Value Stores
Every piece of data is a key pointing to a value. No queries inside the value. Popular: Redis, DynamoDB, Riak. Best for caching, sessions, shopping carts, leaderboards, real-time counters.
Document Stores
JSON-like documents where each record can have a different structure with nested lists and objects. Popular: MongoDB, Couchbase, DocumentDB, Firestore. Best for product catalogs, user profiles, CMS, mobile backends.
Wide-Column
Rows with dynamic columns, built to write and read massive amounts of data very fast. Popular: Cassandra, HBase, Bigtable, ScyllaDB. Best for IoT, time-series, write-heavy logging, recommendation engines.
Graph Databases
Nodes and edges that make traversing relationships lightning-fast. Popular: Neo4j, Amazon Neptune, ArangoDB. Best for social networks, fraud detection, recommendations, knowledge graphs.
4.1 Key-Value Stores
The simplest kind: every piece of data is stored as a key (a unique name, like a locker number) pointing to a value (anything at all — a string, a number, a whole object). There is no query language for searching inside the value; you can only ask “give me whatever is stored under key X.”
Analogy. It’s like a coat check at a theatre. You hand over your coat and get a numbered ticket (the key). To get your coat back, you just show the ticket — the attendant doesn’t care what colour your coat is or what’s in the pockets.
Popular systems: Redis, Amazon DynamoDB, Riak.
Best for: caching, session storage, shopping carts, leaderboards, real-time counters.
4.2 Document Stores
Data is stored as documents — usually in JSON-like format — where each document can have a different structure, and can nest lists and objects inside itself.
Analogy. Like a folder full of forms where every form can be shaped differently — a job application form has different fields than a medical form, but both can sit in the same filing cabinet.
// One "user" document — notice the nested array and object
{
"_id": "u1024",
"name": "Aanya Sharma",
"email": "aanya@example.com",
"addresses": [
{ "type": "home", "city": "Pune" },
{ "type": "work", "city": "Mumbai" }
],
"preferences": { "newsletter": true, "theme": "dark" }
}
Popular systems: MongoDB, Couchbase, Amazon DocumentDB, Firebase Firestore.
Best for: product catalogs, content management, user profiles, mobile app backends.
4.3 Wide-Column (Column-Family) Stores
Data is organised into rows and dynamic columns, but unlike SQL tables, different rows can have completely different sets of columns. These databases are built for writing and reading massive amounts of data extremely fast, especially time-based or event-based data.
Analogy. Imagine a giant spreadsheet where every row is a different customer, but each customer can have their own custom set of columns — one customer might track “last purchase date,” another tracks “loyalty tier” — and the spreadsheet doesn’t mind the mismatch at all.
Popular systems: Apache Cassandra, HBase, Google Bigtable, ScyllaDB.
Best for: IoT sensor data, time-series data, write-heavy logging systems, recommendation engines.
4.4 Graph Databases
Data is stored as nodes (things, like people or products) and edges (relationships between them, like “follows” or “bought”). Graph databases are built specifically to make it lightning-fast to traverse relationships, which relational databases struggle with once relationships get many layers deep (needing many expensive “joins”).
Analogy. Think of a family tree pinned on a wall with strings connecting photos. Finding “who is my cousin’s grandmother” is instant — you just follow the strings. In a spreadsheet version of the same family tree, you’d have to cross-check many separate tables.
Popular systems: Neo4j, Amazon Neptune, ArangoDB.
Best for: social networks, fraud detection, recommendation engines, knowledge graphs.
| Type | Data shape | Great for | Real examples |
|---|---|---|---|
| Key-Value | Key → Value | Caching, sessions | Redis, DynamoDB |
| Document | JSON-like documents | Catalogs, profiles | MongoDB, Firestore |
| Wide-Column | Rows with dynamic columns | Time-series, huge writes | Cassandra, Bigtable |
| Graph | Nodes + edges | Relationships, networks | Neo4j, Neptune |
Architecture & Components
Same set of building blocks, very different design choices — one strong server versus a mesh of equal ones.
Even though SQL and NoSQL databases look different on the surface, both are built from a similar set of building blocks — they just make different design choices with each one.
5.1 Key components in a relational database
- Query planner/optimiser — decides the fastest way to execute your SQL query.
- Storage engine — physically reads and writes data to disk, usually using a B-Tree index structure (a balanced tree that keeps data sorted for fast lookups).
- Transaction manager — enforces ACID rules using logs and locks.
- Write-Ahead Log (WAL) — every change is first written to a log file before touching the actual data, so the database can recover after a crash.
5.2 Key components in a NoSQL database
- Coordinator / Router node — receives a request and figures out which machine holds the relevant data.
- Partitioner — decides how data is split across machines (often using consistent hashing, explained in Section 6).
- Replication manager — keeps multiple copies of each piece of data in sync across different machines and data centres.
- Gossip protocol — many NoSQL clusters (like Cassandra) use “gossip,” where each machine randomly tells a few others “here’s what I know,” so information about the cluster’s health spreads without needing one central manager.
- Consensus module — some systems (like MongoDB, using a Raft-like protocol) use a formal voting process to agree on which copy of the data is the authoritative one, especially when picking a new leader after a failure.
Internal Working — Sharding, Replication & Consensus
How the cluster splits data, keeps safe copies, and agrees on a new leader when one dies.
6.1 Sharding (Partitioning)
Sharding means splitting a huge dataset into smaller pieces (“shards”) and spreading them across many machines, so no single machine has to hold — or process — everything.
Analogy. Imagine sorting a huge pile of mail for an entire country into regional post offices, instead of forcing one office to handle every letter in the nation. Each regional office (shard) only deals with mail for its own area.
A common sharding technique is consistent hashing: every machine and every data key is mapped onto points on an imaginary circle (using a hash function). A piece of data “belongs” to the next machine found by moving clockwise around the circle. The clever part: when you add or remove a machine, only a small slice of data needs to move — not the entire dataset.
6.2 Replication
Replication means keeping multiple identical copies of the same data on different machines, so if one machine dies, others still have the data. There are two common strategies:
- Leader-follower (primary-replica) replication — one node (the leader) accepts all writes and passes them on to follower nodes. Reads can be served by followers too, spreading out the load.
- Leaderless (multi-master) replication — any node can accept a write, and the nodes sync up with each other afterward (used by Cassandra and DynamoDB, favouring availability).
6.3 Consensus and Failure Recovery
When the leader node crashes, the remaining nodes must agree on a new leader without contradicting each other. This coordination problem is solved with consensus algorithms like Raft or Paxos. In simple terms: nodes hold an “election,” each candidate asks for votes, and whichever node gets votes from a majority becomes the new leader. Requiring a majority (not just any node) prevents two different nodes from both believing they are the leader at the same time — a dangerous condition called split-brain.
6.4 Indexing
An index is a separate, sorted structure that lets the database find data quickly without scanning every single record — similar to the index at the back of a textbook. SQL databases mostly use B-Tree indexes. NoSQL databases use a mix, including LSM-Trees (Log-Structured Merge Trees), which are optimised for extremely fast writes by first collecting changes in memory and periodically flushing sorted batches to disk — a design used by Cassandra, HBase, and RocksDB.
Data Flow & Lifecycle of a Request
Step by step: what happens when a mobile app asks to save a new order — first in a NoSQL system, then in a SQL one.
In a traditional SQL system, the flow is more linear: the request goes straight to the single primary server, which checks constraints (like “email must be unique”), writes to the write-ahead log, updates the table and indexes, and only then confirms success — all wrapped inside one transaction that either fully completes or fully rolls back.
Advantages, Disadvantages & Trade-offs
A calm, honest comparison — neither side is a universal winner.
| SQL (Relational) | NoSQL (Non-Relational) | |
|---|---|---|
| Schema | Fixed, defined upfront | Flexible, can evolve freely |
| Scaling | Mostly vertical (bigger machine) | Horizontal (more machines) |
| Consistency | Strong (ACID) | Often eventual (BASE), tunable in many systems |
| Relationships | Excellent — built for joins | Weak or manual — data is often duplicated instead |
| Query flexibility | Very high (SQL is powerful and expressive) | Limited to what the database is optimised for |
| Write throughput at scale | Harder to scale writes | Excellent (many systems built for huge write loads) |
| Best for | Money, inventory, structured business data | Huge scale, flexible/changing data, real-time apps |
| Maturity & tooling | Extremely mature, 50 years of tooling | Newer, growing fast, tooling still maturing |
Disadvantages of NoSQL (be honest)
- Weaker support for complex relationships and multi-record transactions (though modern systems like MongoDB 4.0+ now support multi-document ACID transactions).
- Data duplication is common (the same piece of information may be copied in many documents), which uses more storage and needs careful updates.
- No universal query language — each database has its own way of asking questions, unlike standard SQL.
- Eventual consistency can confuse developers unfamiliar with distributed systems, leading to subtle bugs.
Disadvantages of SQL (also honest)
- Scaling writes horizontally is genuinely hard and often requires complex techniques like manual sharding.
- Schema changes (like adding a column to a table with a billion rows) can be slow and risky in production.
- Rigid structure makes it a poor fit for rapidly evolving or highly varied data shapes.
Performance & Scalability
Two directions to grow — up and out — and how each family handles them.
Scalability is the ability of a system to handle more load by adding resources. There are two directions:
- Vertical scaling (scale up) — give the existing machine more CPU, RAM, or faster disks. Simple, but has a hard physical and financial ceiling.
- Horizontal scaling (scale out) — add more machines and split the work between them. This is where NoSQL databases were designed to shine, since sharding and leaderless replication are built into their core architecture.
Modern SQL databases have narrowed this gap significantly through techniques like read replicas (extra copies used only for reading, to reduce load on the primary), connection pooling, and manual or automated sharding (e.g., Vitess for MySQL, Citus for PostgreSQL). But these are add-ons bolted onto a design that originally assumed a single machine — whereas most NoSQL databases assume a multi-machine cluster from day one.
Latency vs. throughput
Latency is how long a single request takes (e.g., 5 milliseconds). Throughput is how many requests the system can handle per second. NoSQL databases, especially key-value stores like Redis, are optimised for extremely low latency and extremely high throughput on simple lookups — but their throughput advantage can shrink or disappear for complex, relationship-heavy queries, which SQL handles more efficiently thanks to decades of optimiser research.
High Availability & Reliability
How each family stays up when machines fail — and the different first-class values they optimise for.
High availability means the system stays up and usable even when individual parts fail. Both SQL and NoSQL systems achieve this using replication, but NoSQL systems generally treat “always accept the request” as a first-class design goal, while SQL systems generally treat “never show wrong data” as the first-class goal.
10.1 Failover
Failover is the automatic process of switching to a backup system when the primary one fails. In SQL databases, this often means promoting a read replica to become the new primary — a process that can take anywhere from a few seconds to a couple of minutes, sometimes needing manual confirmation. In leaderless NoSQL clusters, there may be no “failover” at all in the traditional sense — since every node can already accept writes, losing one node barely causes a blip.
10.2 Quorum reads and writes
A quorum is the minimum number of nodes that must agree for an operation to be considered successful. Systems like Cassandra let you tune this per-request: for example, requiring only 1 node to acknowledge a write (fastest, least safe) versus requiring all replicas to acknowledge (slowest, safest). This tunability is a superpower of many NoSQL systems — you decide the consistency-vs-speed trade-off per operation, instead of it being fixed for the whole database.
10.3 Disaster recovery & backups
Regardless of SQL or NoSQL, production systems need:
- Regular automated backups, stored in a separate physical location or cloud region.
- Point-in-time recovery — the ability to restore the database to its exact state at, say, 3:14 PM yesterday, useful after accidental data corruption.
- Multi-region replication — keeping live copies of data in geographically distant data centres, so an entire region going offline (fire, earthquake, power grid failure) doesn’t take down the whole service.
- Regular disaster recovery drills — actually practising a full recovery, not just assuming backups will work when needed.
Security
The concerns are the same on both sides; the details differ.
- Authentication — verifying who is connecting (username/password, certificates, or cloud IAM roles). Never leave default admin accounts open on the internet — this is one of the most common causes of exposed MongoDB and Elasticsearch clusters found by security researchers.
- Authorization — controlling what an authenticated user is allowed to do (read-only vs. read-write, which tables/collections they can touch). Follow the principle of least privilege — give each application only the access it truly needs.
- Encryption in transit — using TLS/SSL so data can’t be read if intercepted on the network.
- Encryption at rest — encrypting the actual files on disk, so a stolen hard drive doesn’t leak raw data.
- Injection attacks — SQL databases must guard against SQL injection (malicious input that tricks a query into doing something unintended); NoSQL databases must guard against similar tricks like NoSQL injection in query objects. Always use parameterised queries or an official driver’s safe query builder — never build a query by directly gluing together strings from user input.
- Auditing — logging who accessed or changed what data, and when, for compliance and forensic investigation.
- Network isolation — placing databases inside a private network (VPC), never directly exposed to the public internet.
Monitoring, Logging & Metrics
You cannot fix what you cannot see — the three pillars of database observability.
You cannot fix what you cannot see. Production databases — SQL or NoSQL — need continuous observability across three pillars:
- Metrics — numeric measurements over time, such as query latency, requests per second, replication lag, disk usage, and cache hit ratio. Tools: Prometheus + Grafana, Datadog, AWS CloudWatch.
- Logging — recording individual events, like slow queries, failed authentication attempts, or errors, for later investigation.
- Tracing — following a single request as it travels across multiple services and database calls, useful for finding exactly where time is being lost in a complex system (tools: Jaeger, OpenTelemetry, Zipkin).
Metrics worth alerting on
- Replication lag — how far behind a follower/replica is from the leader; high lag risks serving very stale data.
- Query latency percentiles (p50, p95, p99) — averages hide problems; the 99th percentile shows how bad the worst 1% of requests are.
- Disk and memory usage — running out of disk space is one of the most common causes of a database outage.
- Connection pool saturation — too many open connections can silently exhaust the database’s capacity to accept new work.
Deployment & Cloud
Four common shapes for running a database in production — each with its own trade-off between control and toil.
Very few teams today run a database on a single self-managed server. Modern deployment options include:
- Self-managed on virtual machines — full control, but you own all the operational work (patching, backups, scaling).
- Managed database services — the cloud provider handles patching, backups, and failover automatically. Examples: Amazon RDS/Aurora (SQL), Amazon DynamoDB (NoSQL), MongoDB Atlas, Google Cloud Firestore, Azure Cosmos DB.
- Containerised deployments — running databases inside Docker/Kubernetes, often for development or smaller-scale production, using StatefulSets for stable identity and storage.
- Serverless databases — automatically scale capacity up and down (even to zero) based on traffic, billing only for what’s used — e.g., DynamoDB On-Demand, Aurora Serverless, Firestore.
Cost optimisation tips: right-size your instances instead of over-provisioning “just in case”; use read replicas only where read traffic actually justifies it; archive old, rarely-accessed data to cheaper cold storage; and monitor for unused indexes, which slow down writes without providing read benefits.
Databases, Caching & Load Balancing
Databases rarely stand alone — a real stack layers load balancers, caches, and multiple databases together.
In real systems, databases rarely work alone. A typical production stack layers several pieces together:
Caching stores a copy of frequently-requested data in fast memory, so the app doesn’t need to hit the slower main database every time. A cache hit means the data was found in the cache (fast); a cache miss means it wasn’t, and the app must fetch it from the main database and then store it in the cache for next time.
Load balancing spreads incoming requests evenly across multiple servers so no single server becomes a bottleneck. Databases have their own version of this too — read traffic can be load-balanced across multiple read replicas.
APIs & Microservices — Polyglot Persistence
Each service picks the database that fits its own data shape and access pattern.
In a microservices architecture, an application is broken into small, independent services, each owning its own piece of business logic — and often, its own database. This opens the door to polyglot persistence: using the best-fit database for each service, instead of forcing one database technology to serve every need.
Java example: talking to a document database
Here’s a minimal example showing a Java service saving and retrieving a product document from MongoDB using the official MongoDB Java driver.
import com.mongodb.client.*;
import org.bson.Document;
public class ProductRepository {
private final MongoCollection<Document> products;
public ProductRepository(MongoClient client) {
MongoDatabase db = client.getDatabase("catalog_db");
this.products = db.getCollection("products");
}
// Documents can have different fields — no fixed schema required
public void saveProduct(String id, String name, double price, Document extraAttributes) {
Document doc = new Document("_id", id)
.append("name", name)
.append("price", price)
.append("attributes", extraAttributes);
products.insertOne(doc);
}
public Document findProduct(String id) {
return products.find(new Document("_id", id)).first();
}
}
Java example: talking to a relational database
Compare this to a JDBC example against a SQL database for the order service, where every column is fixed and a transaction guarantees the payment and order row are saved together.
import java.sql.*;
public class OrderRepository {
private final Connection connection;
public OrderRepository(Connection connection) {
this.connection = connection;
}
// Atomic transaction: both inserts succeed, or neither does
public void placeOrder(int customerId, double amount) throws SQLException {
try {
connection.setAutoCommit(false);
try (PreparedStatement orderStmt = connection.prepareStatement(
"INSERT INTO orders (customer_id, total) VALUES (?, ?)")) {
orderStmt.setInt(1, customerId);
orderStmt.setDouble(2, amount);
orderStmt.executeUpdate();
}
try (PreparedStatement paymentStmt = connection.prepareStatement(
"INSERT INTO payments (customer_id, amount, status) VALUES (?, ?, ?)")) {
paymentStmt.setInt(1, customerId);
paymentStmt.setDouble(2, amount);
paymentStmt.setString(3, "CONFIRMED");
paymentStmt.executeUpdate();
}
connection.commit(); // both rows saved together
} catch (SQLException e) {
connection.rollback(); // undo everything on failure
throw e;
}
}
}
Notice the philosophical difference: the MongoDB example happily stores a flexible, evolving attributes field with no fixed shape, while the JDBC example wraps two inserts in a strict transaction because money must never be “half-saved.”
Design Patterns & Anti-Patterns
Habits that make databases sing — and the ones that quietly turn them into a liability.
Good patterns
- Polyglot persistence — using different databases for different services based on their actual needs (see Section 15).
- Denormalisation for reads — in NoSQL, deliberately duplicating data (e.g., storing a customer’s name directly inside an order document) to avoid expensive joins, since NoSQL is optimised for fast reads of self-contained documents.
- Command Query Responsibility Segregation (CQRS) — using a write-optimised SQL database for updates, and a separate read-optimised NoSQL/search database (kept in sync) for fast, flexible querying.
- Event sourcing — storing every change as an immutable event in an append-only log (a natural fit for wide-column or log-based NoSQL stores), and rebuilding current state by replaying events.
- Bounded contexts — letting each microservice own its data model completely, instead of sharing one giant central database.
Anti-patterns to avoid
- Treating NoSQL like a relational database. Designing MongoDB collections with the same normalised, heavily-joined structure you’d use in SQL — this throws away NoSQL’s performance benefits and forces your application to do manual “joins” in code, which is slow and error-prone.
- Using NoSQL for data that needs strict transactions. Storing financial ledgers or inventory counts in a loosely-consistent NoSQL store, then being surprised when two simultaneous purchases both succeed and oversell the last item in stock.
- One database to rule them all. Forcing a single database technology to handle every workload in a large system — search, transactions, caching, analytics — instead of choosing the right tool per job.
- Ignoring your access patterns. In NoSQL modeling, you should design your data shape around how you will query it, not around how it looks conceptually. Modeling data “the way it feels natural” without thinking about real query patterns is one of the most common beginner mistakes, especially in wide-column databases like Cassandra.
Best Practices & Common Mistakes
A concentrated checklist — nothing exotic, but skipping any of it is what makes teams wish they’d picked a different database.
17.1 Best practices
Model NoSQL around your queries
Model your NoSQL schema around your application’s actual read and write patterns, not around abstract “correctness” the way you would with SQL normalisation.
Use indexes deliberately
An index speeds up reads but slows down writes and uses extra storage — don’t index every field blindly.
Tune consistency per operation
Set appropriate consistency levels per operation where the database allows it (e.g., strong consistency for checking stock, eventual consistency for viewing product recommendations).
Version your data shape
Always version your schema/data shape (e.g., include a
schemaVersionfield in documents) so your application can handle old and new formats gracefully as your NoSQL schema evolves over time.Automate and test backups
Set up automated backups and regularly test restoring them — an untested backup is not a real backup.
Alert on replication lag
Monitor replication lag and set alerts before it becomes a customer-facing problem.
Use connection pooling
Use connection pooling in your application to avoid overwhelming the database with new connections.
17.2 Common mistakes
- Choosing NoSQL just because it’s trendy, without a real scaling or flexibility need.
- Choosing SQL out of habit for a workload that is clearly append-only, massive-scale, and doesn’t need relationships.
- Not planning for how data will be deleted or updated when it’s spread across many duplicated documents.
- Under-provisioning replicas, leaving the system one failure away from downtime.
- Skipping load testing before a big traffic event (sales, product launches) and discovering scaling limits in production.
Real-World & Industry Examples
Five well-known companies where SQL and NoSQL sit calmly side by side, each doing what it’s best at.
Cassandra + MySQL
Uses Apache Cassandra for viewing history and personalisation data (needs constant, massive write throughput across the globe), while using MySQL for billing and subscription data (needs strong consistency for money).
DynamoDB + Relational
Uses DynamoDB for the shopping cart (must always accept an “add to cart” even during network issues) and relational databases for order processing and financial reconciliation, where correctness is non-negotiable.
MySQL + Schemaless + NoSQL
Uses a mix — including its own built systems on top of MySQL for core trip and payment data, plus purpose-built stores (like the in-house “Schemaless” layer and various NoSQL systems) for location tracking and high-frequency event data from millions of moving vehicles.
Cassandra + Customised MySQL
Built Cassandra originally for its own inbox search feature (huge write volume across data centres), while user account and social graph data have historically relied on heavily customised MySQL clusters — showing that even “NoSQL-native” companies keep SQL where correctness matters.
Graph-like & specialised stores
Uses graph-like data structures and specialised stores to power “People You May Know” and connection recommendations, where traversing a network of relationships efficiently matters far more than rigid tabular structure.
The Decision Framework: When Should You Actually Choose NoSQL?
The heart of the question — a practical checklist and flowchart to use in the next architecture meeting.
This is the heart of the question. Use the checklist and flowchart below as a practical decision tool.
Choose SQL when…
- Your data has strong, well-defined relationships that benefit from joins (customers, orders, products, invoices).
- You need strict transactional correctness — banking, payments, inventory counts, medical records, ticket booking systems.
- Your schema is stable and well understood upfront.
- You need powerful, flexible ad-hoc querying and reporting across many fields.
- Your team is more experienced with relational modeling, or your data volume doesn’t demand extreme horizontal scale.
Choose NoSQL when…
- Your data shape varies a lot between records or changes frequently (product catalogs with wildly different attributes, user-generated content).
- You expect massive scale — millions of reads/writes per second — that a single powerful server cannot realistically handle.
- You need to spread data across multiple geographic regions with low latency everywhere.
- Your access pattern is simple and predictable (look up by key/ID) rather than needing complex joins across many entities.
- Your workload is relationship-heavy in a network sense (social graphs, fraud rings, recommendation engines) — pick a graph database specifically.
- You need extremely high write throughput for time-series or event data (sensor logs, clickstreams, application logs).
- Slight, temporary staleness in data is acceptable in exchange for speed and constant availability.
FAQ, Summary & Key Takeaways
The questions that come up on day three of every new project, and the sentences worth remembering.
Is NoSQL always faster than SQL?
Not universally. NoSQL is usually faster for simple, key-based lookups and for horizontally scaled write-heavy workloads. But for complex queries involving many relationships, a well-indexed SQL database can outperform a NoSQL database that has to fetch and manually stitch together data from multiple documents in application code.
Can NoSQL databases support transactions?
Yes, increasingly so. Modern document databases like MongoDB support multi-document ACID transactions since version 4.0. However, these transactions are often more limited in scope or slightly more expensive performance-wise compared to a mature relational database’s transaction engine.
Do I have to choose only one type of database for my whole application?
No — and in practice, most large systems don’t. Polyglot persistence (Section 15) is the standard modern approach: pick the right database per service or per data type.
Is SQL “old” and NoSQL “new and better”?
No. They solve different problems well. SQL databases (PostgreSQL, MySQL) are actively developed, constantly improving in performance and scalability, and remain the best choice for a huge share of real-world applications, especially anything involving money or strict rules.
What does “NoSQL” actually stand for today?
Originally it informally meant “no SQL at all.” Today it’s widely understood as “Not Only SQL” — acknowledging that these databases are one more tool in the toolbox, not a total replacement for relational systems.
How do I migrate from SQL to NoSQL (or vice versa) later if I choose wrong?
It’s possible but non-trivial — it usually involves redesigning your data model (not just copying rows into documents), running both systems in parallel temporarily, and carefully migrating traffic. This is exactly why the decision framework in Section 19 is worth spending real time on upfront.
Key Takeaways
- SQL databases store data in rigid tables and guarantee strong correctness (ACID); NoSQL databases store data in flexible shapes and often trade some correctness for speed and scale (BASE).
- The CAP theorem explains why every distributed database must choose between strict Consistency and constant Availability when network problems happen.
- NoSQL comes in four major flavours: key-value, document, wide-column, and graph — each built for a different kind of problem.
- Sharding, replication, and consensus algorithms are the internal engines that let NoSQL databases scale across many machines reliably.
- Choose SQL for strict correctness, complex relationships, and stable schemas. Choose NoSQL for massive scale, flexible or fast-changing data shapes, and simple high-speed access patterns.
- Most real, large-scale systems use both — deliberately, per service — which is called polyglot persistence.
- Security is not optional on either side: authentication, encryption in transit and at rest, and network isolation are baseline expectations, not extras.
- Observability — metrics, logs, and traces — is what separates a database you can operate calmly from one that surprises you at 3 AM.
“The right question is not ‘SQL or NoSQL’ — it’s ‘for this data, in this service, what shape does the truth actually take?’”