Sign in
TodayMapLearnPracticeReview
Library
13 MINadvancedCloud ArchitectureNot started

Index Table

The Index Table pattern creates secondary lookup tables for NoSQL stores that don't natively support secondary indexes. The primary store is keyed by one access pattern (e.g., user_id), but queries often need to look up by other attributes (e.g., email, username, last_login_date). The index table is a separate, smaller table that maps the secondary attribute back to the primary key, maintained on every write. The result: O(1) lookup-by-email on a NoSQL store that would otherwise require a full scan.

Why this matters

NoSQL stores optimize for one access pattern: lookup by primary key. DynamoDB, Cassandra, and many document stores don't have built-in secondary indexes (or charge heavily for them). Without an index table, querying by anything other than the primary key requires a full table scan — infeasible at scale. The Index Table pattern is how teams build multi-access-pattern queries on top of NoSQL stores: maintain as many index tables as you have access patterns. It's the NoSQL equivalent of a database's secondary index, but you own the maintenance and consistency.

Prerequisites
  • SQL Tuning
Related
  • Materialized View
  • CQRS
Used in

Foundational.

Lesson

How it works

NoSQL stores are optimized for primary-key access: get(user_id) → user record. But real applications have many access patterns:

  • get by email — login flow looks up users by email.
  • get by username — profile pages.
  • get by phone — password reset.
  • get by tenant_id — admin views all users in a tenant.
  • get by created_at range — ‘users who signed up this week’.

Without secondary indexes, each of these requires a full table scan — which on a 100M-row table is unacceptable. The Index Table pattern solves this: for each access pattern, maintain a separate table that maps the secondary attribute to the primary key.

Example: a DynamoDB users table keyed by user_id. To support get by email:

code
users table:
  PK: user_id=42
  data: { email: 'alice@example.com', name: 'Alice', ... }

users_by_email index table:
  PK: email='alice@example.com'
  SK: user_id=42

To look up a user by email: query the index table by email → get user_id → query the users table by user_id. Two O(1) lookups instead of a full scan.

The index table can store just the primary key (a sparse index) or a denormalized copy of the full record (a covering index that avoids the second lookup). The trade-off is storage cost vs read latency.

Maintenance: every write to the primary table must also update every index table. If the user changes their email, you must delete the old email-index row and write the new one. This is where it gets tricky in NoSQL — there's usually no transaction across tables, so the updates are eventually consistent.

The hard problem with index tables is consistency. Most NoSQL stores don't support multi-table transactions, so updating the primary table and the index tables in one atomic operation is impossible. Options:

1. Best-effort dual writes — the application writes to the primary table, then to each index table. If any write fails, the index is out of sync. Recovery: a reconciliation job that scans and repairs. Simple but unreliable.

2. Change data capture (CDC) — write only to the primary table; a CDC stream (DynamoDB Streams, Kafka, Debezium) emits the change; consumers update the index tables. Eventually consistent, decoupled from the write path, more reliable.

3. Native secondary indexes — some stores (DynamoDB Global Secondary Indexes, Cassandra materialized views) provide built-in secondary indexes. You pay for them (storage, eventual consistency, update lag), but the store maintains them — no application logic. Often the right answer if your store supports it.

4. Synchronous in-transaction update — if the store supports multi-row transactions (e.g., DynamoDB transactions, FoundationDB), update the primary and index tables in one transaction. Strongly consistent, but slower writes and limited transaction throughput.

The consistency model matters for correctness: if a user updates their email and then immediately logs in with the new email, will the index find them? With eventual consistency (CDC, GSI), the answer is ‘eventually but not immediately.’ For login flows, this usually means falling back to a strongly consistent scan or accepting brief login failure after email change.

A common pattern: use the primary table as the source of truth for writes; use index tables for lookups; accept eventual consistency for most queries; fall back to the primary table for read-your-writes scenarios.

Index Table vs DynamoDB GSI

DynamoDB Global Secondary Indexes (GSIs) are the cloud-managed version of the Index Table pattern. You define a GSI on a table; AWS maintains it asynchronously, eventually consistent. You pay for storage and for the GSI's read/write capacity. The trade-off vs a hand-rolled Index Table: GSI is operationally simpler (no application code to maintain) but less flexible (you can't customize the consistency model or the projection attributes as finely). Most teams should use GSIs when available; hand-roll Index Tables only when the native option is too limited or too expensive.

Design considerations for index tables:

  • Which attributes to index? Index attributes that you actually query by. Each index costs storage and write amplification. Don't index attributes you never query.
  • Sparse vs covering index? Sparse: just the secondary key → primary key mapping. Cheaper storage, but requires a second lookup to get the full record. Covering: denormalize the full record into the index. More storage, but one lookup serves the read.
  • Sort key / clustering? For range queries (users created this week in a tenant), use a composite key: PK=tenant_id, SK=created_at. This allows range scans within a partition.
  • Cardinality matters. Indexing a low-cardinality attribute (e.g., gender) gives huge partitions and limited benefit. Indexing a high-cardinality attribute (e.g., email) gives precise lookups.
  • Hot keys. If one index key maps to many records (e.g., one tenant with millions of users), that partition becomes a hotspot. Consider sharding the sort key.
  • TTL / cleanup. Index tables can accumulate stale rows. Use TTLs or scheduled cleanup to remove index rows whose primary record no longer exists.
  • Rebuilding. If an index drifts (bug, failed writes), you need a way to rebuild it from scratch — typically a batch job that scans the primary table and writes the index.

The discipline is the same as for relational indexes: index for your actual query patterns, not for theoretical completeness. Every index has a cost; only pay it where it earns its keep.

Check yourself
interview

You have a DynamoDB `users` table keyed by `user_id`. You need to look up users by email for login. Without an index table, what's the cost, and how does an index table change it?

Pick one answer.

Check yourself
interview

What is the main consistency challenge when maintaining index tables in a NoSQL store that doesn't support multi-table transactions?

Pick one answer.

Engineering mental model

Mental model. Think of Index Table as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does Index Table mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”

Design lens

Before choosing Index Table, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.

Original NO CAP systems visual for Index Table.
Image unavailable. Original NO CAP systems visual for Index Table.
Index Table: a compact system-thinking visual.— Original NO CAP visual.
SELECT id, created_at
FROM records
WHERE tenant_id = ?
ORDER BY created_at DESC
LIMIT 50;

-- Ask: which index makes this query predictable at scale?
A minimal engineering sketch for reasoning about Index Table.

Back-of-the-envelope reasoning

Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.

Interactive sandboxdeterministic

Interactive thought experiment: Index Table

Change the variables below and predict what breaks first in Index Table. The production lab can later reuse these same inputs.

System pressure6%
Try this

Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.

Hint

If you are stuck on Index Table, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.

Check yourself
solid

You increase traffic by 10× in a system using Index Table. What should you inspect first?

Pick one answer.

Check yourself
interview

Which statement is the safest engineering habit when using Index Table?

Pick one answer.

Try this
interview

You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.

Production scenario: your system uses Index Table, traffic suddenly spikes, and p99 latency doubles. What is your first move?

Interview drill

Answer this without notes: When would you choose Index Table, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.

Engineering lens

For Index Table, start with access patterns rather than brand names. Identify the dominant reads/writes, data relationships, consistency requirements, partition key, hot keys and failure behavior before choosing a storage strategy.

Numerical sanity check

A rough capacity check: required write throughput ≈ peak writes/s × average record size. At 5,000 writes/s and 2 KB average payloads, the raw incoming data stream is about 10 MB/s before indexes, replication and overhead.

Check yourself
interview

Do not optimize for a memorized definition. Reason from the workload and failure mode.

A team proposes Index Table because it 'scales'. What workload characteristic would make that choice a poor fit?

Pick one answer.

Trade-offs

What you gain, what you pay

Pros
  • +Enables O(1) lookups by attributes other than the primary key on NoSQL stores.
  • +Avoids full table scans for non-primary-key queries.
  • +Multiple index tables support multiple access patterns from one primary store.
  • +Index tables can be denormalized (covering indexes) to avoid the second lookup.
  • +Decouples read optimization from the primary store's design.
Cons
  • −Write amplification — every write updates the primary table plus N index tables.
  • −Consistency challenges — without multi-table transactions, indexes can drift from the primary.
  • −Storage cost — index tables duplicate data.
  • −Operational complexity — you own the maintenance, reconciliation, and rebuild logic.
  • −Schema evolution is harder — index changes require rebuilding.
Failure modes

How this breaks in production

  • Index drift — a failed write leaves the index out of sync; reads return wrong or stale data.
  • Hot partition in an index — one index key (e.g., a huge tenant) becomes a hotspot.
  • Orphaned index rows — index points to a primary key that no longer exists.
  • Index rebuild takes too long — large indexes can take hours to rebuild from scratch.
  • Schema mismatch — primary table's schema evolved; index wasn't updated.
  • Throttling under high write volume — index writes consume the primary table's capacity.
Common mistakes

Don't fall into these traps

  • •Indexing attributes you never query by — pay the cost, get no benefit.
  • •Forgetting reconciliation — without periodic scans, drift accumulates silently.
  • •Using best-effort dual writes without a recovery plan — failures leave indexes inconsistent.
  • •Not having a rebuild strategy — when an index drifts, you must be able to rebuild from scratch.
  • •Treating index tables as strongly consistent — they're eventually consistent unless using transactions.
  • •Ignoring hot keys — a single high-cardinality index key (one tenant) can become a hotspot.
Where you see it

Real systems using this

DynamoDB applications with GSIs or hand-rolled index tables for email/username/phone lookups.Cassandra applications with materialized views for secondary access patterns.Cosmos DB applications with change-feed-driven index tables.Wide-column stores without native secondary indexes.Any NoSQL system where multiple access patterns must be supported.
Teardowns

How real systems implement this

  • AWS DynamoDB Global Secondary Indexes (GSIs) — DynamoDB's cloud-managed implementation of the Index Table pattern. You define a GSI with an alternate partition/sort key; AWS maintains it asynchronously, eventually consistent, with its own read/write capacity. Most teams use GSIs instead of hand-rolling.
  • Cassandra Materialized Views — Cassandra's built-in secondary-index implementation. The coordinator maintains the view on every write to the base table. Has known limitations around consistency and hot partitions, but for many use cases is simpler than hand-rolled index tables.
  • Cosmos DB Change Feed → Azure Search — A common pattern: Cosmos DB emits changes via its change feed; a consumer updates an Azure Search index, which acts as an Index Table for full-text and complex queries. The change feed provides reliable, eventually-consistent index maintenance.
Interview prompts

Practice saying it out loud

  • Q1What is the Index Table pattern, and why is it needed for NoSQL stores?
  • Q2You have a DynamoDB table keyed by user_id. Login needs to look up by email. How do you design this?
  • Q3What are the consistency challenges of maintaining index tables, and how do you mitigate them?
  • Q4When should you use a native secondary index (e.g., DynamoDB GSI) vs a hand-rolled index table?
  • Q5What happens if a write to the primary table succeeds but the corresponding index write fails? How do you detect and fix this?
Research

Further reading & references

System Design Primer
Open source
ByteByteGo — Scale from zero to millions
ByteByteGo
System Design Tutorial
GeeksforGeeks
System Design Roadmap
roadmap.sh
Cloud Architecture reference
Reference
Cloud Architecture reference
Reference
Cloud Architecture reference
Reference

Core explanations are original NO CAP material. External references are provided for deeper study and standards.

What next?

Mark as understood once the mental model clicks.

Next recommended

Materialized View