SwadhyAI Notes

All System Design notes

System Design Data

Types of Databases

Picture building something like Uber: payments must never double-charge, sessions must feel instant, driver search needs geo filters, and chat history grows without mercy. One engine rarely makes every one of those questions cheap.

A database type is mostly a bet about which questions should be boring, meaning cheap, fast, and predictable every time. engines make ledgers boring, stores make session lookups boring, and a makes relevance ranking boring. This page walks through each family, what it makes cheap, what it makes expensive, and how to decide with measurements rather than brand loyalty.

Decision questions

1

What is the dominant question?

Joins, fetch by id, relevance, or append by time: each favours a different engine.

2

Do multi-row transactions matter? If money or inventory moves, they usually do.

3

Growth path

Grow the primary with replicas and a cache first; specialists come later.

4

Every new engine needs someone to back it up and answer its pages.

What actually happens

The Uber-style product above asks a different question on every screen, and each question wants a different storage shape. Checkout wants transactions (all-or-nothing writes across several rows, so a payment and its order line never disagree). The session cookie wants a microsecond get: hand over one key, get one value back, no query planning. Discovery wants a for text relevance or a index for "things similar to this". Telemetry wants a append path that never has to update old rows.

Because no single engine makes all of those cheap, teams often run several side by side, a habit called . The important discipline is that only one of them is the source of truth (the primary store, whose copy of a fact wins any argument). Caches and search indexes are derived from it and can be rebuilt from it, which is what makes them safe to lose. The price of every extra engine is covered by question 3 in the next section.

Interview-ready thinking therefore starts with the access pattern (which query runs most, and how fast it must be), not the brand name. The next section turns that instinct into three concrete questions and one rule of thumb.

How to choose a database type

With the access-pattern mindset in place, three questions settle most database decisions before any brand comes up.

1. What are the top queries? Name the top five queries and their latency SLOs (service-level objectives, the response-time targets you promise), then say whether joins, keyed lookups, relevance ranking, or time-ordered appends dominate. That single answer eliminates most families.

2. Do writes need multi-row transactions? Ledgers and inventory usually stay because a transfer touches two rows that must both change or neither. Flexible catalogs, where each record carries different fields, may fit stores. Also ask whether the data model is stable, flexible per tenant, graph-shaped, time-shaped, or search-shaped; each shape points at a family.

3. Can the team operate another fleet? Every additional engine needs backups, version upgrades, and someone on call when it pages at 3 a.m. Only add a specialist when the access-pattern win is large enough to pay for all three.

Simple rule of thumb

Default to for the source of truth. Add caching, then or warehouse tech (a separate analytics store) when measurement shows pain. Prefer one strong primary over premature sprawl.

In simple terms: pick the engine that makes your hottest question boring, and that your team can run at 3 a.m.

Match the engine to the question

Compare engines using a concrete access contract: the keys supplied, predicates used, maximum result size, latency target, write rate, and correctness boundary. A product catalog may need flexible document reads, full-text search, and transactional stock updates, but these requirements do not force three databases immediately. A capable primary engine may support all of them initially. Add a specialist when measured benefit justifies a second data lifecycle.

Question 1 above asked which query dominates. The flow below maps each common answer to the family that makes it cheap, and every branch converges on the same final step: you still have to run the thing.

Dominant workload

Need joins, constraints, transactions?

Yes

Relational or distributed SQL

Mostly fetch by key?

Yes

Key-value or document

Text relevance or facets?

Yes

Search engine

Append by time?

Yes

Time-series or wide-column

Many relationship hops?

Yes

Graph database

Operate, back up, test failure modes

Whichever branch the flow lands on, every engine in a production stack plays one of three roles, and knowing the role tells you what it is allowed to be bad at.

The primary store is optimized for correctness, recovery, and writes you cannot lose; it is the copy that wins any disagreement. A derived store (a search index, an analytics table, or a read model shaped for one screen) is optimized for fast reads and is rebuilt from the primary whenever possible, so losing it is an inconvenience rather than data loss. A cache is optimized for latency and load shedding (absorbing traffic so the primary does not have to), and its expiry and invalidation rules define how correct its answers are.

Relational (RDBMS)

The rule of thumb said to start here, so the tour starts here too. A relational database management system (RDBMS) stores data in tables, queries it with SQL, and enforces constraints such as uniqueness and foreign keys for you.

Two flavours you will meet

A feature-rich relational engine adds semi-structured columns (JSON stored inside a table cell), full-text search, spatial extensions for geo queries, and MVCC (multi-version concurrency control, which lets readers proceed without blocking writers). A well-tuned single node handles a surprising amount of traffic; the Numbers to know table below gives the order of magnitude.

An operationally simple relational engine offers mature replication and straightforward operational patterns. Large social and marketplace workloads often pair it with sharding routers (a layer that decides which server holds which rows), read replicas, and cache layers rather than replacing it.

When relational shines

  • ACID transactions are required, as in payments, orders, and inventory that must never double-count.
  • Complex queries join many tables, which is exactly what analytics and reporting usually need.
  • The schema must be enforced strictly, as regulated industries often demand for audit purposes.
  • Relationships between entities are central to the domain, so joins are the natural query shape.

How it scales when one node is not enough

  • Read replicas (read-only copies) scale read-heavy paths first, before you attempt to split writes.
  • Sharding by user or tenant splits rows across servers when write or storage limits finally arrive.
  • A router layer hides shard maps and tenant moves from application code, so services stay simple.

Document databases

Relational tables assume every row has the same columns. When records genuinely differ from one another, a document store removes that assumption instead of fighting it.

A document store keeps flexible JSON-like documents, shards them automatically (spreads them across servers by a shard key you choose), and supports rich queries inside a document. It is at its best when data varies per record, as in product catalogs, user profiles, and content management, and it can scale to very large datasets when the shard key matches how the application actually reads.

When documents shine

  • The schema varies per record, such as product catalogs where attributes differ by category.
  • Data is hierarchical or nested, like a user profile with its addresses embedded inside.
  • Teams iterate rapidly and want schema changes without running a migration each time.
  • Reads dominate and queries stay simple, with no complex JOINs across collections.

The poor fits are heavy multi-document transactions and SQL-style joins, which document engines either lack or make slow. Watch index explosion too: every query shape you want fast needs its own index, and each one taxes writes.

Key-value stores

Documents still let you query inside a record. Key-value stores go one step simpler: give me a key, hand back the value, and skip query planning entirely. That simplicity is what buys their speed.

Two flavours you will meet

An in-memory key-value cache holds everything in RAM and answers in sub-millisecond time. Beyond plain strings it offers hashes, sorted sets, streams, and approximate counters, and it sustains very high operations per second on one node. Teams use it as a cache, a session store, a rate limiter, a pub/sub channel (publish a message once, every subscriber receives it), and a leaderboard. Clustered deployments scale by partitioning keys across nodes.

A managed key-value table is a hosted, partitioned table with low-latency keyed access. It reaches very high request rates when keys distribute evenly across partitions, capacity can be provisioned up front or driven by demand, and global replicas support multi-region patterns.

When key-value shines

  • Caching sessions, API responses, or computed results that are expensive to produce again.
  • Simple lookups by primary key, such as loading one user profile by its ID.
  • High throughput with low latency, where a sub-millisecond response is the actual requirement.
  • Real-time features such as leaderboards, rate limiting, and counters that update constantly.

The poor fit is using it as the primary store for ad hoc relational queries or relevance search: there is no planner to answer a question you did not key for in advance.

Wide-column, graph, time-series

The three families so far cover most applications. The next three exist because some workloads have a shape (enormous append volume, deep relationship chains, or time-ordered measurements) that general engines handle badly.

Wide-column

Wide-column databases in this context usually group records by a partition key and order data within that partition for predictable queries. They are not the same as analytical columnar storage, which groups values from the same column to compress and scan them efficiently. The similar names hide different access patterns: one often serves keyed operational reads and appends, while the other scans many records but only selected columns.

Designed for massive write throughput across datacenters. Rows are partitioned by a row key and clustered by column, so everything under one partition key lives together and a write to it stays local and fast. Best for time-series, IoT (sensor) feeds, messaging, and other append-heavy workloads at very large scale.

Graph

Following one stored adjacency reference can be cheap, but a graph query still pays for every explored edge, filtered property, and network boundary. A search three hops out with branching factor 100 may explore up to roughly a million candidate paths before deduplication and pruning. Supernodes and cross-partition edges can dominate performance. Estimate the traversal frontier rather than assuming every multi-hop query is constant time.

A graph database represents entities as nodes and relationships as edges, often with efficient adjacency access. This can make relationship-heavy traversals easier than repeated joins, but total work grows with the edges explored, filters evaluated, and partition crossings. It fits workloads such as relationship analysis, recommendations, and dependency graphs when their traversal patterns justify the specialized model.

Time-series

Optimized for append-heavy, time-ordered data. Downsampling (replacing old fine-grained points with coarser summaries) and retention policies (dropping data past an age) run automatically. Fits IoT telemetry, application metrics, and financial tick data, and the specialized storage sustains very high append rates.

Vector, search, distributed SQL

Two of the last three families are derived indexes: they answer "what is relevant?" or "what is similar?" and are rebuilt from a primary store. The third tries to give a relational database horizontal scale without giving up SQL.

Vector

Vector search retrieves items near a query embedding according to a distance or similarity measure. Approximate indexes exchange some recall for lower search cost; recall measures how many true nearest neighbors the search recovered. Embedding model changes, metadata filters, deletion handling, and index rebuilds are part of the design. Measure retrieval quality and latency together because a fast answer that omits relevant documents may fail the product requirement.

Stores and searches high-dimensional embeddings (lists of numbers a model produces so that similar meanings land close together). Approximate nearest neighbor (ANN) search returns close matches in milliseconds across billions of vectors, powering semantic search, recommendations, retrieval workflows, and image similarity. Because it is derived, you own its freshness lag and the dual-write surface (the risk that primary and index are updated separately and drift).

Search

An inverted index for full-text search: each token maps to the list of documents containing it, so relevance queries return in under a second across large corpora. Powers autocomplete, fuzzy matching, faceted search (count-by-category filters beside results), and log analytics.

Distributed SQL

SQL plus horizontal scaling plus strong consistency. It uses consensus (nodes vote to agree on each write before acknowledging it), range movement (shifting key ranges between nodes to rebalance), and sometimes bounded clock uncertainty. Best when you need SQL semantics across regions and can pay the coordination cost on every write.

Numbers to know: scaling characteristics (illustrative)

Having met every family, here is the one table of rough throughput figures the rest of the page refers back to. TPS means transactions per second; ops/sec means individual reads or writes per second.

DatabaseRough single-node throughputTypical scale-out lever
Relational SQLThousands to low tens of thousands TPSReplicas, partitioning, sharding routers
Document storeHigh ops/sec when keyed wellBuilt-in sharded clusters
In-memory cacheVery high in-memory ops/secHash slots or key partitioning
Wide-columnWrite-heavy linear scale-outAdd nodes, tune consistency
Managed key-value tableElastic per provisioned or demand-driven capacityPartitions auto-split
Search engineQuery throughput scales with shardsMore data nodes / shards
Distributed SQLRegional/global SQL with consensus overheadAdd regions / ranges

The pattern to notice: relational engines scale reads with replicas far more easily than they scale writes, while many NoSQL designs make write scaling simpler by narrowing the query model to what one partition can answer.

Decision matrix

With the families and their rough numbers in hand, this matrix lines up common use cases against the family that usually fits, so you can answer "what would you use for X?" quickly and then justify it.

Use caseOften fitsExample
User accounts + paymentsRelationalRelational SQL
Product catalog (varied schema)DocumentDocument store
Session cache + rate limitingKey-value (often in-memory)In-memory key-value
Very high write chat streamsWide-columnWide-column store
Social graph traversalsGraphGraph database
IoT sensor dataTime-seriesTime-series database
Semantic / embedding searchVectorVector database
Full-text product searchSearch engineSearch index
Global strongly consistent SQLDistributed SQLConsensus SQL
Managed key-value tablesHosted wide-column KVManaged partitioned table

Fit checks and hidden costs

A derived store needs a freshness target, a replay source, and a rebuild plan. If a search index loses data, can it be reconstructed from the primary store without stopping new updates? If an analytics schema changes, can old events still be interpreted? Account for duplicated bytes, indexing work, operational ownership, and recovery time. These costs can outweigh a specialist engine’s faster query for a modest workload.

The matrix says what usually fits. This table adds the other half of the decision: when a family is the wrong tool, and what it quietly costs you even when it is the right one.

FamilyStrongest fitDo not use whenHidden cost
Relational SQLConstraints, joins, ledgers, inventoryWrite scale needs many independent partitions immediatelyIndex bloat, migrations, single-primary write ceiling
DocumentFlexible nested records and product catalogsCross-document invariants dominateDuplicated facts and stale denormalized views
Key-valuePrimary-key lookups, sessions, rate limitsAd hoc filters and joins matterHot keys, TTL correctness, cache stampedes
Wide-columnPartition-local high write volumeAccess patterns are unknown or join-heavyShard key mistakes, repair work, consistency tuning
Search / vectorRelevance, facets, similarityIt is the only source of truth for money pathsFreshness lag, reindex cost, replay pipelines

A few of those hidden-cost terms deserve a plain definition. Denormalized views are copies of a fact stored in several places for fast reads, which go stale when the original changes. A hot key is one key that receives a disproportionate share of traffic and overloads its partition. TTL (time to live) is the expiry on a cached entry, and a cache stampede is many callers recomputing the same expired entry at once and crushing the primary behind it.

The same picture, one line per family

  • Relational: joins and transactions; scale reads with replicas, writes with tuning and then sharding.
  • Document: flexible nested reads; watch index explosion and any need for cross-document transactions.
  • Key-value and wide-column: partition-local access at huge scale, but weak at ad hoc queries.
  • Search and vector: derived indexes, so budget for repair lag and the dual-write surface.

Sizing questions before you commit

Before choosing, estimate row width, index count, retention, QPS (queries per second), cache hit rate, replica count, hot-key skew, and rebuild time. Remember that the same logical object stored in a primary store, a cache, a search index, and an analytics read model exists four times, and every copy multiplies storage and repair work.

Polyglot stacks (examples)

The fit checks explain why large products end up with several engines. Here is what that looks like in practice, with each engine doing the one job it makes cheap. OLTP means online transaction processing, the live store the application writes to; OLAP means online analytical processing, the aggregates and reports run over that data.

Media platform split (illustrative)

  • Wide-column: high-volume user-centric writes, the append path a single primary could not absorb.
  • Relational: billing and strongly consistent ledgers, where a double charge is unacceptable.
  • In-memory cache: edge caching layers that keep hot reads away from the primary.
  • Search: discovery and logs, both relevance-and-filter questions rather than keyed lookups.
  • Data lake tables: analytics over files in object storage, kept off the live path.

Marketplace split (illustrative)

  • Document layer: wide-column document patterns, giving flexible records at partition scale.
  • Wide-column: real-time telemetry shaped workloads, which are append-heavy by nature.
  • In-memory cache: caching and coordination between services, where latency matters most.
  • Search index: search and observability indexing, both derived from other stores.
  • Real-time OLAP: analytical aggregates served fast enough for dashboards.
  • Relational SQL: legacy transactional services that still need joins and constraints.

Million-user database decision sheet

Those polyglot stacks were built by teams who had already outgrown one engine. This sheet is for the moment before that, when you are asked to design for a million users and need to decide whether one strong primary is still enough.

Million-user workload

Need constraints or multi-row transaction?

Yes

Mostly fetch by id?

Yes

Need text/vector relevance?

Yes

Append-heavy stream?

Yes

Each branch ends with the follow-up job it creates. The two lists below turn the flow into a yes-or-no test, where QPS means queries per second.

Use one strong primary when

  • Rows are modest, joins matter, and write QPS is within one primary's envelope.
  • Read QPS can be handled with indexes, caches, and read replicas alone.
  • Correctness depends on uniqueness, foreign keys, balances, or inventory invariants.

Add a specialist when

  • The query model is stable and maps cleanly to one partition key.
  • Search, vector similarity, or analytics need an index shape the primary should not own.
  • Hot rows, append volume, or regional latency exceed what replicas and caches can hide.

Try it: map workloads to database families

Each card describes a dominant access pattern. Match it to the simplest store before reaching for polyglot persistence. Reveal checks your choice against the page’s decision sheet.

Selected workload
Orders and ledger
Cards explored
Guess first

Pick a workload card, guess the primary store family, then reveal the mapping and read the caution about ops cost.

Relational remains the default source of truth. Specialized stores accelerate one read model; they rarely remove the need for reconciliation.

Name the derived copy. Search indexes and feeds are projections. Say how CDC or events keep them fresh and how you detect drift.

Wrap-up

You have seen the families, the numbers, and the decision sheet. Here is how to compress all of it into an interview answer, where to read the primary sources, and the one idea to keep.

How to explain this in an interview

  • Open with the rule of thumb above, then name the concrete workload that would change it.
  • Add a specialized read model only after naming the access pattern it accelerates.
  • Explain how data moves between stores: CDC (change data capture, streaming row changes out of the primary), events, backfill, reconciliation, and replay.
  • State the failure mode you accept: stale reads, duplicate writes, search lag, cache stampede, or hot partition.

Primary sources

Key takeaway

Default to a capable engine unless access patterns clearly diverge. Grow it with a cache, then or analytics tech when measurement shows pain, and demand a large incremental win before accepting sprawl, because the ops cost from question 3 never goes away.

Practice and further reading

Use these exercises to connect the lesson to a design decision. State your assumptions, trace what happens to one request, and explain the rule that must remain true even during a failure. That rule is the design's correctness invariant.

Focus for this lesson: Match relational, document, key-value, wide-column, graph, time-series, search, and vector stores to specific query and operational needs.

Explain the decision

Why is “polyglot persistence” not automatically good architecture?

Read the reasoning

Every specialist store adds synchronization, backup, security, expertise, failure, and decommissioning cost; require a measured workload advantage.

Apply it to a production scenario

Similarity search is added to a transactional catalog. Should the vector store own products?

Read one approach

Keep the transactional catalog authoritative; publish versioned embeddings to a vector index and define staleness, rebuild, deletion, and fallback behavior.

Work through the numbers

100 million 1,536-dimension float32 vectors require how much raw vector memory?

Read the worked calculation

100M × 1,536 × 4 bytes ≈ 614.4 GB before index structures, metadata, replicas, and quantization.

Reason through a failure

What must a derived search or vector index support?

Read the recovery reasoning

Versioned rebuild, replay from an authority, deletion/privacy propagation, and a safe fallback when the index is stale or unavailable.