Skip to content
Laviyx Learn

Try

System Design course menu

SQL vs NoSQL: How to Choose a Database

Compare relational databases with key-value, document, wide-column, search and graph stores, understand ACID and BASE, indexes, and how to choose for a system design.

Intermediate5 min readUpdated
On this page

Choosing how to store data is one of the most important design decisions, because data is the hardest part to change later.

Relational databases#

Relational databases, such as PostgreSQL and MySQL, store data in tables with fixed columns, and connect them with keys. You query them with SQL.

They provide ACID transactions:

LetterMeaning
AtomicAll steps happen, or none do
ConsistentRules, such as unique emails, always hold
IsolatedConcurrent transactions do not interfere
DurableSaved data survives a crash

These suit money, orders, and anything with relationships. Modern PostgreSQL handles far more than many beginners assume, and for most projects it is a good first choice.

NoSQL families#

TypeModelExamplesGood for
Key-valueKey to valueRedis, DynamoDBSessions, caches, counters
DocumentJSON-like documentsMongoDBVaried or nested data
Wide-columnRows with flexible columns, partitionedCassandra, HBaseHuge write volumes, time series
SearchInverted indexElasticsearch, OpenSearchFull-text search, logs
GraphNodes and edgesNeo4jRelationships, recommendations
Time seriesTimestamped pointsInfluxDB, TimescaleDBMetrics

Many NoSQL systems follow BASE (basically available, soft state, eventually consistent), giving up some guarantees to scale out easily.

SQL or NoSQL?#

Choose relational whenChoose NoSQL when
Data has relationships and joinsAccess is simple, by key
You need transactionsWrite volume is enormous
Questions are unpredictableSchema varies a lot
Scale fits one primary and replicasData must span many nodes easily

In interviews, the good answer is not "NoSQL scales better". Say what the access pattern is, and why a database fits it.

Large systems often use several, known as polyglot persistence: orders in PostgreSQL, sessions in Redis, search in Elasticsearch, and files in object storage.

Indexes#

An index is a sorted structure that speeds up lookups, like the index in a book. Without one, the database scans every row:

Python
import bisect
import time

rows = list(range(0, 2_000_000, 2))
target = 1_999_998

start = time.perf_counter()
found = target in rows
scan = time.perf_counter() - start

start = time.perf_counter()
pos = bisect.bisect_left(rows, target)
indexed = time.perf_counter() - start

print("Both found it:", found and rows[pos] == target)
print("Index was faster:", indexed < scan)

steps = 0
low, high = 0, len(rows)
while low < high:
    mid = (low + high) // 2
    steps += 1
    if rows[mid] < target:
        low = mid + 1
    else:
        high = mid
print(f"Rows: {len(rows):,}. Full scan checks up to {len(rows):,}. Binary search took {steps} steps.")
Output
Both found it: True
Index was faster: True
Rows: 1,000,000. Full scan checks up to 1,000,000. Binary search took 20 steps.

Real databases use B-trees, which work on the same idea. Indexes make writes a little slower, and they take space, so index what your queries need. See SQL indexes.

Normalisation and denormalisation#

Normalised data avoids duplication, and uses joins. Denormalised data copies fields to make reads fast. At scale, joins get expensive, so read-heavy systems often store a precomputed copy, accepting that it must be kept up to date.

Text
Normalised:    posts(id, user_id, text)  +  users(id, name)
Denormalised:  posts(id, user_id, user_name, text)

Common mistakes#

Practice#

Practice exercise

Choose a database type for each: (a) shopping cart sessions, (b) bank transfers, (c) product search with typos allowed, (d) billions of sensor readings. Then check the logic of the last one: how many steps does a binary search take on 1 billion sorted rows?

Show solution

(a) Key-value, such as Redis. (b) Relational, for ACID transactions. (c) A search engine, such as Elasticsearch. (d) Wide-column or time series.

Python
import math

print(math.ceil(math.log2(1_000_000_000)))
Output
30

Key takeaway#

Next: Database Replication.

Finished this lesson?

Some links are affiliate links. If you buy through them, Laviyx Learn may earn a commission at no extra cost to you. As an Amazon Associate, we earn from qualifying purchases.