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.
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:
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=42To 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.
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.
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.
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?”
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.
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?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 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.
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.
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.
You increase traffic by 10× in a system using Index Table. What should you inspect first?
Pick one answer.
Which statement is the safest engineering habit when using Index Table?
Pick one answer.
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.
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.
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.
What you gain, what you pay
- +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.
- −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.
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.
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.
Real systems using this
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.
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?
Further reading & references
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