HV
home / core-cs / dbms

DBMS & NoSQL

ACID, normalization, indexing, isolation levels, and the SQL vs NoSQL decision — including the NoSQL data-modeling questions SDE 2 interviews love.

1. ACID vs BASE

ACID (traditional RDBMS)
  • · Atomicity — transaction is all-or-nothing
  • · Consistency — DB moves between valid states only
  • · Isolation — concurrent transactions don't interfere
  • · Durability — committed data survives crashes
BASE (typical NoSQL)
  • · Basically Available — system responds, always
  • · Soft state — state may change without input (replication lag)
  • · Eventually consistent — replicas converge over time, not instantly

2. Normalization

FormRuleRemoves
1NF Each column holds atomic (indivisible) values; no repeating groups. Multi-valued columns, e.g. a 'phone_numbers' CSV string in one cell.
2NF 1NF + every non-key column depends on the whole primary key (not part of a composite key). Partial dependency on a composite key.
3NF 2NF + no non-key column depends on another non-key column. Transitive dependency, e.g. storing city AND zip_code -> city depends on zip, not the key.
BCNF Every determinant (column that determines another) is a candidate key. Edge cases 3NF misses with overlapping candidate keys.

Trade-off: Normalization removes redundancy and update anomalies but requires JOINs to reassemble data (slower reads). Denormalization (common in NoSQL) duplicates data for fast reads at the cost of write complexity and potential inconsistency.

3. Indexes

B-Tree Index (most common — sorted, balanced tree): [ 50 ] / \ [ 20 ] [ 80 ] / \ / \ [10] [30,40] [60,70] [90,100] Lookup, range scan, and sorted order all O(log n) - this is why B-Tree (not hash) is the default index type: it supports "WHERE age > 30" range queries, hash indexes only support exact-match "WHERE age = 30".
Clustered Index

Table rows are physically stored in index order. Only one per table (usually the primary key). Fast for range scans on that key.

Non-Clustered Index

Separate structure storing a pointer back to the actual row. Can have many per table. Extra lookup hop ("bookmark lookup") to fetch full row data.

Cost of indexes: Every index speeds up reads but slows down writes (INSERT/UPDATE/DELETE must update every index too) and takes extra storage. Don't over-index write-heavy tables.

4. Transaction Isolation Levels

LevelDirty ReadNon-Repeatable ReadPhantom Read
Read Uncommitted Possible Possible Possible
Read Committed Prevented Possible Possible
Repeatable Read Prevented Prevented Possible (MySQL InnoDB mostly prevents via gap locks)
Serializable Prevented Prevented Prevented

Dirty read: reading another transaction's uncommitted change. Non-repeatable read: re-reading the same row gives a different value because another transaction committed a change in between. Phantom read: re-running the same query returns a different set of rows because another transaction inserted/deleted matching rows.

5. SQL vs NoSQL

AspectSQL (RDBMS)NoSQL
Schema Fixed schema, defined upfront Flexible / schema-less
Scaling Vertical (harder to shard, though modern SQL can scale-out) Horizontal by design (sharding built in)
Consistency Strong (ACID) Usually eventual (BASE), tunable in some (e.g. Cassandra, DynamoDB)
Relationships JOINs handle relational data naturally Denormalized — relationships embedded or resolved in app code
Query flexibility Rich ad-hoc queries (SQL) Query patterns often need to be known upfront (data modeled around access patterns)
Best for Financial systems, inventory, anything needing strong consistency + complex queries High write throughput, huge scale, flexible/evolving schema, simple access patterns
Key-Value Store
Redis, DynamoDB, Memcached

Simple key -> opaque value (blob).

→ Caching, session storage, feature flags — O(1) lookups by key.

Document Store
MongoDB, Couchbase

Key -> JSON/BSON document with nested fields.

→ Catalogs, CMS, user profiles — flexible, evolving schemas per record.

Column-Family Store
Cassandra, HBase, Bigtable

Rows grouped by partition key; columns can vary per row.

→ High write throughput, time-series, huge scale (e.g. Twitter tweets, IoT data).

Graph Database
Neo4j, Amazon Neptune

Nodes + edges with properties.

→ Social graphs, recommendation engines, fraud detection — relationship-heavy queries.

6. NoSQL Data Modeling: Design for Access Patterns

SQL mindset: model entities/relations first, then write queries (JOINs at query time) NoSQL mindset: list your query access patterns FIRST, then shape data to match them Example - "Get all orders for a user, sorted by date" (Cassandra/DynamoDB): Partition Key: user_id Clustering Key: order_date (descending) +------------------------------------------------------+ | user_id=123 | order_date=2026-08-20 | order_id=A1 ... | | user_id=123 | order_date=2026-08-18 | order_id=A2 ... | | user_id=123 | order_date=2026-08-10 | order_id=A3 ... | +------------------------------------------------------+ Single partition read, no JOIN, already sorted -> O(1) partition lookup + range scan. If you ALSO need "get all orders for a restaurant", you denormalize: write the same order into a second table partitioned by restaurant_id (this is why NoSQL write paths often fan-out to multiple tables).

Rule of thumb: in NoSQL, duplicate data across tables/collections to match each read pattern with a single-partition lookup. The cost moves from "expensive JOIN at read time" to "extra writes to keep copies in sync" — worth it when reads vastly outnumber writes.

7. NoSQL & DBMS Interview Questions

Q: When would you choose MongoDB over Cassandra for the same feature?

A: MongoDB when the access patterns are still evolving and you need rich queries (secondary indexes, aggregation pipeline) on moderate write volume. Cassandra when you have very high, roughly linear-scaling write throughput (e.g. IoT/event ingestion, time-series) and your query patterns are fixed and known upfront (partition + clustering key design).

Q: How does Cassandra achieve high write throughput?

A: Writes go to an in-memory memtable + append-only commit log (sequential disk write, no random I/O), then get flushed to immutable SSTables on disk asynchronously. No read-before-write and no locking — this write path is why Cassandra scales writes near-linearly with more nodes.

Q: What is the CAP trade-off for MongoDB vs Cassandra vs DynamoDB?

A: MongoDB (single primary per replica set) leans CP — writes go through a primary, so it can be strongly consistent, but is unavailable for writes during primary election. Cassandra is tunably AP/CP via consistency levels (e.g. ONE, QUORUM, ALL) — you trade off latency for consistency per query. DynamoDB is AP by default (eventually consistent reads) but supports opt-in strongly consistent reads at higher latency/cost.

Q: How do you model a many-to-many relationship (e.g. students-courses) in a document DB?

A: Either embed an array of course IDs in the student document (and vice versa) if the lists are small and bounded, or create a separate 'enrollment' collection with student_id + course_id if either side can grow unbounded (avoids the MongoDB 16MB document size limit and unbounded array growth issues).

Q: What is a hot partition and how do you avoid it in Cassandra/DynamoDB?

A: When a single partition key receives disproportionate traffic (e.g. one viral restaurant_id, or a naive date-only partition key that funnels all of today's writes to one partition) it becomes a throughput/storage bottleneck on that one node. Fix: choose higher-cardinality partition keys or add a random/hashed suffix ('sharding key') to spread writes, then fan-out reads across the shards.

Q: Why doesn't NoSQL support JOINs, and what do you do instead?

A: Data is deliberately partitioned/sharded across nodes for horizontal scale — a JOIN would require expensive cross-node data movement, defeating the scalability goal. Instead: denormalize (duplicate data to match access patterns), embed related data in the same document, or do the 'join' in application code with multiple targeted lookups (usually still cheaper than a distributed JOIN).

Q: Explain the difference between a dirty read and eventual consistency.

A: A dirty read (SQL isolation concept) is reading another transaction's uncommitted, possibly-to-be-rolled-back data within the same system. Eventual consistency (distributed systems concept) is reading committed but stale data from a replica that hasn't yet received a recent write — the data was valid and committed, just not yet propagated.