Databases in System Design: SQL vs NoSQL, Replication and Sharding
System Design & ArchitectureLesson 4 of 66 min read
What you'll learn
- Compare SQL and NoSQL databases and explain ACID vs BASE
- Explain how indexes speed up reads and what they cost on writes
- Use leader-follower replication for read scaling and understand replication lag
- Choose a sharding key and avoid hot partitions and cross-shard pain
On this page
In an e-commerce checkout, the order must be saved exactly once, stock must go down by exactly the right amount and a payment must never be charged twice. Meanwhile the product catalogue is read a thousand times more often than it changes. Different data has different needs, and the database choice is where those needs meet reality.
SQL vs NoSQL: choosing by access pattern
Relational (SQL) databases such as PostgreSQL and MySQL store data in tables with a fixed schema, support joins and offer strong transactional guarantees. NoSQL is an umbrella term for several different models: key-value stores, document databases, wide-column stores and graph databases. They typically trade rich querying and strict guarantees for flexible schemas and easier horizontal scaling.
| SQL (relational) | NoSQL (varies by type) | |
|---|---|---|
| Data model | Tables, rows, foreign keys | Key-value, documents, wide-column, graph |
| Schema | Defined up front, enforced | Flexible or schema-on-read |
| Queries | Rich: joins, aggregates, ad hoc | Optimised for known access patterns |
| Transactions | Multi-row ACID transactions are standard | Often limited to a single item or partition (varies) |
| Scaling | Vertical and read replicas first; sharding is manual work | Designed to partition across many nodes |
| Good fit | Orders, payments, inventory, anything relational | Sessions, feeds, event logs, catalogues, time series |
A sound default is to start with a relational database. It handles far more load than many people expect, and you can always move specific high-volume data to a specialised store later. Reach for NoSQL when you have a clear reason: a simple access pattern at very large scale, flexible document shapes, or write volumes that a single relational node cannot absorb.
ACID vs BASE
ACID describes the guarantees of a classic transaction: Atomicity (all steps happen or none do), Consistency (the data respects its rules after the transaction), Isolation (concurrent transactions do not see each other's half-finished work) and Durability (committed data survives a crash). Checkout is a textbook case: reducing stock, creating the order and recording the payment must succeed or fail together.
BASE (Basically Available, Soft state, Eventually consistent) describes the looser model many distributed NoSQL systems adopt: the system stays available even when nodes disagree for a while, and replicas converge over time. A "like" counter or a view count can tolerate being off by a few for a few seconds; a bank balance cannot. Many real systems use both, each for the data that suits it.
Indexes: faster reads, slower writes
Without an index, finding all orders for one user means scanning every row. An index (commonly a B-tree) is a sorted structure that lets the database jump straight to matching rows. The trade-off is that each index uses storage and must be updated on every insert, update and delete, so more indexes mean slower writes.
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
status VARCHAR(20) NOT NULL,
total_cents BIGINT NOT NULL,
created_at TIMESTAMP NOT NULL
);
-- Composite index: filter by user, then sort by newest
CREATE INDEX idx_orders_user_created
ON orders (user_id, created_at DESC);
-- Served efficiently by the index above
SELECT id, status, total_cents
FROM orders
WHERE user_id = 42
ORDER BY created_at DESC
LIMIT 20;Design indexes from your real queries. Put equality filters first and the sort column after, keep the index narrow, and check the plan with EXPLAIN instead of guessing. Index columns you filter or join on; do not index every column.
Replication: scaling reads and surviving failures
In leader-follower replication, one node (the leader) accepts writes and copies them to followers, which serve reads. This scales read-heavy workloads, gives you standby copies for failover and lets you run backups and analytics away from the main node.
writes
|
v
+------------+
| Leader |
+------------+
| | | replicate (usually async)
v v v
+------+ +------+ +------+
| Rep1 | | Rep2 | | Rep3 | <-- reads
+------+ +------+ +------+The catch is replication lag. With asynchronous replication a follower can be slightly behind, so a user who just updated their profile might read the old version from a replica. Common fixes are to read the user's own recent writes from the leader ("read your writes"), or to route that session to the leader for a short time. Synchronous replication removes the lag but makes every write wait for followers and hurts availability if one is slow.
Sharding: splitting data across nodes
Replication copies the same data everywhere, so it cannot help when the data or the write rate outgrows one machine. Sharding (horizontal partitioning) splits the data so each node holds only a slice. The most important decision is the shard key, the field that decides where a row lives.
App server
| shard = hash(user_id) % 3
v
+--------+ +--------+ +--------+
| Shard0 | | Shard1 | | Shard2 |
| users | | users | | users |
| A-slice| | B-slice| | C-slice|
+--------+ +--------+ +--------+
(each shard normally has its own replicas)| Approach | How | Pros | Cons |
|---|---|---|---|
| Range-based | Key ranges per shard (A-H, I-P, ...) | Efficient range scans | Prone to hot spots (e.g. newest IDs all land on one shard) |
| Hash-based | hash(key) mod N picks the shard | Even spread | Range queries touch all shards; changing N moves most keys |
| Consistent hashing | Keys and nodes placed on a ring | Adding a node moves only a small share of keys | More complex to implement and operate |
| Directory-based | A lookup table maps key to shard | Maximum flexibility | The directory is a bottleneck and a SPOF unless made reliable |
Plain hash(key) % N is easy but brittle. Going from 3 shards to 4 changes the result for about three quarters of all keys, which means moving most of your data. Consistent hashing, or a fixed larger number of logical partitions mapped to physical nodes, avoids that.
Hot partitions
Even a hash key can create a hot partition. If you shard a social app by user_id, one celebrity account with millions of followers sends disproportionate traffic to its shard. If you shard by created_at, every new write hits the latest shard. Choose a key with high cardinality and even access, and for known hot keys add a random suffix ("salting") to spread them, at the cost of reading from several places.
Common mistakes
- Choosing NoSQL because it "scales" without a reason. Symptom: you rebuild joins and transactions by hand in application code, with bugs.
- Missing or wrong indexes. Symptom: queries get slower as the table grows, and one slow query saturates the database.
- Over-indexing. Symptom: slow inserts and updates, and large storage bills from indexes that no query uses.
- Reading from a replica right after a write. Symptom: users save a change and then see the old data. Route read-your-writes traffic to the leader.
- A poor shard key. Symptom: one shard runs hot while others are idle, so adding shards does not help.
- Forgetting backups and failover. Replication is not a backup: a bad delete is replicated to every follower too.
How this shows up in interviews and real projects
Expect questions such as "SQL or NoSQL for this, and why?", "how would you scale the database?" and "what is your shard key?". Answer by naming the access patterns and consistency needs first, then the choice. In real projects most teams stay on a single relational primary with replicas for a long time, and shard only the data that forces it. The next lesson, CAP Theorem and Consistency Models, explains what happens when replicas cannot agree. See also the system design interview prep guide.
Try it yourself
For an e-commerce platform, choose a database type for each: user accounts, orders and payments, product catalogue, shopping cart sessions, and product view counters. Justify each choice with the access pattern and consistency requirement. Then pick a shard key for orders and explain one hot-partition risk.
Show a hint
Orders and payments need ACID transactions (relational). Sessions and counters fit a key-value store. Sharding orders by user_id keeps one user's orders together, but a bulk buyer or reseller could create a hot shard.
Key takeaways
- Choose the database from access patterns and consistency needs; relational is a strong default, and NoSQL is a specific tool for specific needs.
- ACID gives all-or-nothing transactions; BASE accepts temporary inconsistency in exchange for availability and scale.
- Indexes make reads fast and writes slower; design them from real queries and verify with EXPLAIN.
- Read replicas scale reads but introduce lag; sharding scales writes and storage but adds heavy complexity.
- A good shard key has high cardinality and even load; watch for hot partitions.
Test yourself
A trick from this lesson's ride: ACID: all-or-nothing, rules kept, no peeking, survives a crash. Four more tricks like it wait along the route, each followed by a question.
Frequently asked questions
When should I use SQL vs NoSQL?
Use SQL when your data is relational and you need joins, ad hoc queries and multi-row transactions, such as orders and payments. Use NoSQL when your access pattern is simple and known, you need flexible schemas, or write volume and data size exceed what a single relational node can handle.
What is database sharding and when is it needed?
Sharding splits a dataset across multiple database nodes by a shard key so each node stores and serves only part of the data. It is needed when write throughput or data size outgrows a single machine, after simpler options such as indexing, caching and read replicas have been used.
What is the difference between ACID and BASE?
ACID provides strict transactional guarantees: atomicity, consistency, isolation and durability. BASE relaxes them to stay available and scalable: basically available, soft state and eventually consistent. ACID suits money and inventory, while BASE often suits feeds, counters and caches.