A write-heavy OLTP table has accumulated fourteen indexes, one added for each new report over three years, and insert latency has been climbing. How would you decide what the right number of indexes for that table is?
answer
- no magic number — derive from the write budget
- measure the curve at 14 / 10 / 6 indexes
- every index needs a named query + owner
- working set vs RAM = where latency bends
- 14 report indexes on OLTP = wrong place for reporting
basics
~20 sThere is no magic number. Derive it from the table's write service level: measure the per-index write cost, attribute each index to a named query with a business owner, drop what nothing claims, consolidate overlapping ones, and move report-only access to a replica or a separate model.
solid answer
~1 minI would reject the question as stated — "the right number" is an output, not an input — and derive it instead. 1. **Set the constraint.** What write latency and throughput does this table owe its callers? That budget, not a rule of thumb, decides how much index maintenance is affordable. 2. **Measure the cost.** Establish per-write index maintenance today: structures touched per insert, log volume, working set versus RAM, and how much of write time is index work. On a test copy, measure throughput at 14, 10, and 6 indexes to get the actual curve. 3. **Attribute every index to a query and an owner.** Any index without a named, frequent, selective consumer is a drop candidate. Anything unclaimed after a full business cycle goes. 4. **Consolidate.** Remove prefix-redundant indexes, merge overlapping ones with the same leading columns, and replace broad indexes on skewed columns with restricted ones. 5. **Move the workload.** Fourteen indexes for reports on an OLTP table is an architecture smell: reporting belongs on a read replica or a separate analytical store, where index count costs the write path nothing. 6. **Institutionalise it.** New indexes require a named query and an owner; index review becomes part of schema change review.
go deeper
Say that each index adds write cost, that unused ones should be found via usage statistics and removed, and that reports may not belong on the transactional table.
Add measurement of index maintenance share, prefix redundancy and consolidation, and the observation window needed before trusting usage counters.
Run the full attribution and reduction programme with measured before/after numbers, safe rollout, and a considered position on the rare-but-critical index.
Set the write budget as the governing constraint, find the knee in the cost curve empirically, treat report-driven indexes as an architecture problem to relocate rather than tune, and install the process and telemetry that keep the count from regrowing.
## Why "how many indexes is too many?" has no numeric answer Asking for a number invites a rule of thumb, and rules of thumb here are wrong because the cost of an index depends on write rate, key distribution, row width, whether the working set fits in memory, and replication topology — while the benefit depends on query frequency, selectivity, and how much latency those queries owe their users. Two tables with identical index counts can be perfectly healthy and completely broken. The right framing is a **budget**: the table has a write service level, index maintenance consumes it, and each index must buy its share of that consumption with read value. ## Establishing the constraint Start from what the write path owes: peak insert/update rate, the latency percentile that matters, and headroom for growth and for failover scenarios where one node carries more. That is the denominator. Without it, every subsequent argument is aesthetic. ## Measuring the current cost Quantify rather than assert: - Structures modified per write, and index maintenance as a share of total write time. - Write-ahead log volume per transaction, which determines both local I/O and replication lag. - Total index size versus table size versus available memory. The single most important threshold is whether the combined hot pages of the table and its indexes still fit in RAM. Past that point each additional index causes physical reads on the write path, and the latency curve bends sharply. - Background reclaim work, which scales with index count on update-heavy tables. Then get the actual shape of the curve: clone to a test environment, replay a representative write workload at 14, 10, 6, and 3 indexes. The result is usually not linear, and knowing where the knee sits converts the debate into arithmetic. ## Attribution: every index needs a claimant Run the usage-counter exercise across every node for a full business cycle — long enough to include monthly and quarterly jobs. For each index produce: which queries use it, how often, what latency they owe, and who owns them. Then classify: - **Constraint-bearing** (primary key, unique): not negotiable, they are data integrity. - **Hot-path**: serving frequent, latency-sensitive, selective queries. Keep. - **Rare but critical**: the quarterly extract. Keep, or accept a slower scan for it and drop — an explicit tradeoff, not an accident. - **Unclaimed**: nothing uses it, or its only user is a query nobody owns anymore. Drop. - **Redundant/overlapping**: prefix-redundant pairs, exact duplicates, or sets consolidatable into one wider index. The attribution step is where fourteen usually becomes six or seven without anyone losing anything they care about. ## The architectural read The detail that matters most in this scenario is *why* there are fourteen: one per report. That is analytical demand being served from a transactional table, and adding indexes is the wrong answer to it. Every report index taxes every insert forever, so the write path subsidises reporting queries permanently. The structural fixes: - **Read replica for reporting.** Indexes needed only by reports can live on a replica — though note the replica still applies the primary's writes, so this helps only where indexes can differ per node, which depends on the engine. - **A separate analytical store** fed by change data capture or a periodic extract, modelled for the reports rather than for the transactions. This is the real fix when reporting demand is genuinely growing, and it also removes long analytical queries from the OLTP node. - **Materialised summaries** for the handful of reports that are aggregations, refreshed on a schedule, which removes both the index and the query from the hot path. - **Partitioning** where reports are time-bounded, so each report touches a small number of partitions and needs less index support. ## Making the decision durable Without process, the count grows back. What holds: - **A rule that a new index requires a named query, an expected frequency, a selectivity estimate, and an owner** — reviewed like any other schema change. - **Index review inside schema change review**, where adding a fifteenth index requires arguing against the write budget explicitly. - **Telemetry that shows cost next to benefit**: index size, maintenance share, and usage counters on one dashboard, so a dead index is visible rather than inferred. - **A periodic sweep** — quarterly — using the same attribution method, so drift is caught while it is cheap. ## Executing the reduction Do it incrementally, largest-cost-first, one index at a time, with an invisible/disable trial where the engine supports it, the exact creation statement scripted for rollback, and monitoring on both sides: the queries that might regress and the write metrics that should improve. Report the outcome in the terms the budget was set in — "p99 insert latency fell from 18ms to 7ms, index maintenance dropped from 61% to 24% of write time" — because that is what makes the next reduction easy to authorise. ## What separates a strong answer Refusing the number, naming the write budget as the real constraint, insisting on measurement over assertion, and recognising the architectural smell that fourteen report indexes on an OLTP table represents. A weaker answer picks a number like five and defends it.
- How do you handle the index that serves only a quarterly regulatory report?Make the tradeoff explicit rather than defaulting either way. Price the cost — what that index adds to every write for a year — against the cost of the report running as a scan four times a year, and against the option of running it on a replica or a snapshot copy. Often the right answer is dropping it and accepting a slower quarterly job, but it should be a decided position with the report's owner, not a silent removal.
- You drop eight indexes and write latency barely improves. What does that tell you?That index maintenance was not the binding constraint, and the measurement step was skipped or done badly. Look elsewhere: log flush and commit durability, lock or latch contention on hot pages, a saturated storage device, replication back-pressure, or checkpointing. It is also a reminder to reverse anything you removed that had genuine read value, since you paid a read cost for no write gain.
- What prevents the count from growing back to fourteen?Process plus visibility. Require a named query, expected frequency, selectivity, and an owner for every new index, reviewed as part of schema change review. Put index size, maintenance share, and usage counters on a dashboard so dead indexes are visible. Run a quarterly sweep using the same attribution method, and give the reporting workload a proper home so the pressure to add report indexes disappears.
Asking how many indexes a table should have is like asking how many people a car should carry. The answer is not a number, it is the payload the suspension is rated for.
saying these in an interview costs you the question
- Answering with a fixed number such as "no more than five indexes per table" as if it were a law.
- Proposing drops without first measuring how much of write time index maintenance actually consumes.
- Ignoring that fourteen report-driven indexes signal analytics running on an OLTP table.
- Dropping in bulk with no rollback plan and no per-index monitoring.
- Treating unique and primary-key indexes as candidates for the same cost-benefit trade as performance indexes.
- Assuming the relationship between index count and write latency is linear rather than finding the knee.