Notes
What SQL is
Start with what SQL is. A relational database keeps data in tables. Every row has the same columns, and the schema is fixed up front. Postgres and MySQL are the ones you'll name in interviews.
Its first superpower is joins. Users and orders live in separate tables, and one query combines them. You can ask questions nobody planned for when the schema was designed. That flexibility of queries is the point of SQL.
Its second superpower is transactions. Move five hundred rupees from one account to another: two updates, and the database guarantees both happen or neither does, even if the server crashes halfway. That guarantee is called ACID, and it's why money and orders live in SQL.
The cost. A relational database is happiest on one machine. You scale reads with replicas, but spreading writes across many machines, called sharding, is manual work, and once you shard, joins and transactions across shards stop working. That's the real limit, not speed.
users id | name | email 1 | Priya | p@x.com 2 | Arjun | a@x.com orders id | user_id | total 7 | 1 | 499
SELECT u.name, o.total FROM users u JOIN orders o ON o.user_id = u.id WHERE o.total > 100; → any question across tables, without planning it in advance
BEGIN; UPDATE accounts SET balance = balance - 500 WHERE id = 1; UPDATE accounts SET balance = balance + 500 WHERE id = 2; COMMIT; both happen, or neither (ACID)
- one machine holds all the data
- scale up first: bigger box
- read replicas help reads
- splitting writes across machines
- (sharding) is manual and hard:
- joins and transactions stop
- working across shards
What NoSQL is
Now NoSQL. It isn't one thing, it's four families, and the name only says what they're not. Key-value stores like Redis and DynamoDB. Document stores like MongoDB. Wide-column stores like Cassandra. And graph databases like Neo4j.
Key-value is the simplest: give me the value for this key, in one hop. Document stores keep whole records as nested JSON. Two users can have different fields, and the record you read is the record your app uses, no joins needed.
Wide-column stores group rows by a partition key and sort them, which makes them great for massive write volume in time order, like chat messages or sensor data. Graph databases store relationships directly, so friends of friends is one cheap query instead of a chain of joins.
key-value:
"session:8f3a" → { user: 1, … }
one lookup, by key, O(1)
document:
{ id: 2, name: "Arjun",
addresses: [ … ],
prefs: { dark: true } } wide-column: partition key: chat_id rows sorted by time → huge write volume, time-ordered graph: nodes + edges → friends of friends, in one query
Why NoSQL scales
Here's why NoSQL scales out easily. Every record has a partition key, and the store uses it to decide which machine owns the record. Writes for different keys go to different machines, so adding machines adds capacity. Sharding is built in, not bolted on.
The cost. There are no joins across partitions, so you must know your access patterns before you design the keys. And many NoSQL stores are eventually consistent by default: read right after a write, and you might see the old value for a moment. You trade query freedom and strict consistency for scale.
- no joins across partitions
- queries must follow the key
- (know your access pattern first)
- consistency is often eventual:
- a read right after a write
- can return the old value
The myth
Time for the myth. NoSQL is not faster. A well-indexed Postgres handles tens of thousands of queries a second on one machine. A NoSQL store is fast for the exact access pattern it was designed around, and slow, or impossible, for anything else. Speed comes from matching the store to the pattern.
- Postgres serves 10k+ queries/sec
- on one machine, with indexes
- NoSQL is faster only for the
- access pattern it was built for
- wrong pattern → slower than SQL
How to choose
So how do you choose? Ask four questions. One: do records relate to each other, and do I query across them? Two: do I need all-or-nothing updates across several records? Three: will the questions change later, or do I know every query today? Four: is write volume beyond one machine, or is the data naturally nested?
The rule. Yes to relationships, transactions or unknown queries: SQL, and Postgres by default. A known access pattern with huge write volume or a nested shape: NoSQL, and pick the family that fits. If you're unsure, start with Postgres. It's the safe answer, and interviewers know it.
- 1. Do records relate to each other,
- and do I query across them?
- 2. Do I need all-or-nothing updates
- across several records?
- 3. Will the questions change,
- or do I know every query today?
- 4. Is write volume beyond one
- machine, or is the shape nested?
- yes to 1, 2 or 3 → SQL
- (Postgres by default)
- known access pattern +
- huge writes or nested shape
- → NoSQL
- unsure → start with Postgres
Examples
Some examples. Orders and payments: SQL, for transactions. User profiles with varying fields: a document store. Sessions and caches: key-value. Chat messages and logs, written constantly in time order: wide-column. A social graph: graph. A URL shortener: key-value, one lookup by code.
- orders, payments → SQL
- user profiles → document
- sessions, cache → key-value
- chat messages, logs → wide-column
- social graph → graph
- URL shortener → key-value
Use both
And the answer interviewers like most: use both. A real e-commerce system keeps orders in Postgres, sessions and hot data in Redis, and product search in Elasticsearch. One store per job. Say that, and you've shown you design for the access pattern, not for a slogan.
In the interview
In the interview, say it like this. I'd start with Postgres, because orders need transactions and we'll query them in ways we can't predict. If chat messages grow past one machine, I'd move that table to Cassandra, partitioned by chat_id. Store, reason, and the trigger to change. That's a senior answer.
- “I’d start with Postgres: orders
- need transactions and we’ll query
- them in ways we can’t predict.
- If chat messages grow past one
- machine, I’d move that table to
- Cassandra, partitioned by chat_id.”