Indexes Overview
IntermediateIndexes are data structures that speed up row lookup at the cost of write overhead and storage; InnoDB uses B-tree indexes for most index types.
Overview
Indexes are the single most impactful performance tool in MySQL. A B-tree index is a balanced tree where leaf nodes store the indexed column values and pointers to rows; a lookup traverses O(log n) levels instead of scanning every row. InnoDB has two index types: the clustered index (primary key) — which IS the table data, stored in B-tree leaf nodes ordered by PK — and secondary indexes, which store the indexed columns plus the primary key as a row locator. Because secondary indexes point to the PK (not a physical row address), primary key changes trigger cascading secondary index updates. Understanding this distinction explains many InnoDB performance characteristics: sequential PK inserts are fast (append to clustered index); random UUID PKs cause page splits and fragmentation.
Index types and when to use each
InnoDB supports several index types. Knowing when each is appropriate prevents over-indexing (write overhead) and under-indexing (slow reads).
-- PRIMARY KEY (clustered index): the table IS the index
-- Rows stored in PK order on disk; all secondary indexes include PK
CREATE TABLE orders (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, -- clustered
...
);
-- Single-column secondary index: speeds up equality and range on one column
CREATE INDEX idx_status ON orders (status);
-- Composite (multi-column) index: leftmost prefix rule applies
-- Covers: status, status+customer_id, status+customer_id+created_at
CREATE INDEX idx_status_customer_date ON orders (status, customer_id, created_at);
-- UNIQUE index: enforces uniqueness + fast lookup (same as regular index + constraint)
CREATE UNIQUE INDEX idx_email ON users (email);
-- FULLTEXT index: relevance-based text search with MATCH() ... AGAINST()
CREATE FULLTEXT INDEX idx_description ON products (description);
SELECT * FROM products WHERE MATCH(description) AGAINST ('wireless headphones' IN BOOLEAN MODE);
-- Prefix index: index only first N characters (saves space for long VARCHAR)
CREATE INDEX idx_url_prefix ON pages (url(50));
-- Cannot be used for ORDER BY or GROUP BY (not covering)Write overhead and choosing which columns to index
Every index must be maintained on INSERT, UPDATE, DELETE. Over-indexing hurts write performance more than under-indexing hurts read performance for write-heavy tables.
-- EXPLAIN output: key to diagnosing index usage
EXPLAIN SELECT * FROM orders WHERE status = 'PENDING' AND customer_id = 42;
-- Look for:
-- type: ALL (full scan — bad), ref (index lookup — good), range, const
-- key: which index was chosen (NULL = no index)
-- rows: estimated rows scanned
-- Extra: "Using index" (covering), "Using filesort" (no sort index)
-- Index selectivity: high cardinality = better index
SELECT COUNT(DISTINCT status) / COUNT(*) AS selectivity FROM orders;
-- 0.001 = 0.1% (bad — status has few unique values)
-- 0.99 = 99% (good — almost every row has a unique value)
-- Rule of thumb for composite index column order:
-- 1. Equality predicates first (WHERE status = 'PENDING')
-- 2. Range predicates last (WHERE created_at > '2024-01-01')
-- 3. Sort column last if possible (ORDER BY created_at)
-- Check actual index usage in production
SELECT * FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE object_schema = 'mydb' AND object_name = 'orders'
ORDER BY count_read DESC;
-- Indexes with count_read=0 are unused — candidates for removalClustered vs secondary index and write amplification
Understanding InnoDB's clustered index structure explains why UUID primary keys hurt performance and why secondary index lookups are more expensive than primary key lookups.
-- InnoDB clustered index: table data IS the B-tree
-- PK row: [id=1 | customer_id=42 | status='PENDING' | total=99.99]
-- Secondary index on (status):
-- Leaf node: [status='PENDING' | id=1] ← stores PK, NOT physical row address
-- Extra lookup: to get full row from secondary index:
-- 1. Look up status='PENDING' in secondary index → get PK=1
-- 2. Look up PK=1 in clustered index → get full row
-- This is a "double lookup" or "row fetch" — avoided by covering indexes
-- UUID PK problem: random insertion order
-- [INSERT id=uuid-3] → page split (new page needed between existing pages)
-- [INSERT id=uuid-1] → another split
-- Page splits cause fragmentation, wasted space, slower inserts
-- Solution: use BIGINT AUTO_INCREMENT (sequential, append-only)
-- Or: UUID v7 (time-ordered) / ULID (lexicographically ordered) if UUID is needed
-- Measure fragmentation
SELECT table_name,
ROUND(data_length/1024/1024, 2) AS data_mb,
ROUND(data_free/1024/1024, 2) AS free_mb,
ROUND(data_free/(data_length+index_length)*100,1) AS frag_pct
FROM information_schema.tables
WHERE table_schema = 'mydb' AND table_name = 'orders';
-- Rebuild to defragment (locks table!)
ALTER TABLE orders ENGINE=InnoDB; -- or: OPTIMIZE TABLE orders;Key Points to Remember
- 1The clustered (primary key) index IS the table — leaf nodes store full row data in PK order.
- 2Secondary indexes store indexed columns + PK; reading a full row requires a second lookup into the clustered index.
- 3Random UUID PKs cause B-tree page splits and fragmentation; use BIGINT AUTO_INCREMENT or time-ordered UUIDs.
- 4High-cardinality columns (email, ID) make better indexes than low-cardinality ones (boolean, status with 3 values).
- 5Over-indexing is a real problem — every index adds overhead on INSERT/UPDATE/DELETE; remove unused indexes.
- 6Use EXPLAIN and performance_schema.table_io_waits_summary_by_index_usage to identify missing and unused indexes.
Interview Questions
Sign in to ask AriaWhat is the difference between a clustered index and a secondary index in InnoDB?
Why do UUID primary keys hurt INSERT performance and what alternatives exist?
What does "Using filesort" in EXPLAIN mean and how do you eliminate it?
How do you identify unused indexes in a production MySQL database?
Explain the leftmost prefix rule and how it determines which parts of a composite index are used.
Ask Aria about Indexes Overview
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.