Database Indexes
debt(d5/e3/b5/t5)
Closest to 'specialist tool catches it' (d5). Missing or suboptimal indexes are detected via EXPLAIN analysis, slow query logs, or database profiling tools. The term mentions sys.schema_unused_indexes (MySQL) and pg_stat_user_indexes (PostgreSQL) — these are specialist diagnostic tools, not default linters. A missing index won't cause syntax errors or be caught by standard code review without deliberate EXPLAIN analysis.
Closest to 'simple parameterised fix' (e3). The quick_fix shows adding an index is a single CREATE INDEX statement or $table->index('column') in migrations. However, fixing index issues often requires identifying the problem queries first, potentially adding indexes across multiple tables, and sometimes restructuring composite indexes — slightly more than a one-line patch but still localized database changes.
Closest to 'persistent productivity tax' (b5). Index strategy affects query performance across the entire application. Poor indexing decisions compound over time as data grows — a query that worked fine with 10k rows becomes a bottleneck at 1M rows. The term notes write-heavy tables suffer from over-indexing, meaning index decisions create ongoing tension between read and write performance. This is a persistent architectural consideration but not system-defining.
Closest to 'notable trap' (t5). The misconception field explicitly states 'Adding more indexes always improves performance' — this is a documented gotcha that most developers eventually learn. Developers intuit that indexes help queries but don't initially understand the write overhead, leading to over-indexing. The LIKE '%keyword%' trap and composite index column ordering are additional documented gotchas that catch intermediate developers.
Also Known As
TL;DR
Explanation
A database index is a separate data structure (typically a B-tree) maintained alongside a table that maps column values to row locations. Without an index, every query on a column requires a full table scan — every row is read regardless of how many match. With an index, the engine traverses a balanced tree in O(log n) time to find matching rows directly. Index types: B-tree (default, works for equality, range, ORDER BY, and LIKE 'prefix%'); hash (equality only, faster for exact lookups); composite (covers multiple columns — column order matters); covering (includes all columns a query needs, avoiding a table lookup entirely); partial (indexes a subset of rows based on a condition). MySQL's EXPLAIN and PostgreSQL's EXPLAIN ANALYZE show whether queries use indexes and where they scan. The most common PHP performance issue is a missing index on a frequently-queried foreign key or WHERE clause column.
Watch Out
Common Misconception
Why It Matters
Common Mistakes
- Missing indexes on foreign key columns — ORMs create foreign key constraints but do not always create indexes; add indexes manually on every FK column.
- Using LIKE '%keyword%' and expecting an index — a leading wildcard disables B-tree index usage; use FULLTEXT indexes for text search.
- Over-indexing write-heavy tables — each index adds overhead on every write; audit and remove unused indexes.
- Wrong column order in composite indexes — a composite index on (a, b) helps queries on a and on (a, b) but not queries on b alone; put the highest-cardinality column first.
Avoid When
- Avoid indexing columns with very low cardinality (boolean, status with 2–3 values) — the planner often skips them in favour of a full scan.
- Do not add indexes speculatively — each index slows INSERT, UPDATE, and DELETE and consumes storage. Add them in response to measured slow queries.
- Avoid over-indexing write-heavy tables — a table with 15 indexes on a high-insert workload will bottleneck on index maintenance.
When To Use
- Index every foreign key and any column that appears in WHERE, JOIN ON, or ORDER BY clauses in frequent queries.
- Use composite indexes when queries filter on multiple columns together — column order matters; put the most selective column first.
- Use covering indexes to eliminate table lookups when a query selects only indexed columns.
Code Examples
-- No index on user_id — full table scan on every order lookup
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT, -- missing index
status VARCHAR(20), -- missing index
created_at DATETIME
);
-- EXPLAIN shows: type=ALL, rows=500000 — scans entire table
-- Indexed foreign key + composite for common query pattern
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT NOT NULL,
status VARCHAR(20) NOT NULL,
created_at DATETIME NOT NULL,
INDEX idx_user_id (user_id),
INDEX idx_status_created (status, created_at) -- composite for ORDER queries
);
-- EXPLAIN now shows: type=ref, rows=12 — uses index
-- In Laravel migration:
-- $table->index('user_id');
-- $table->index(['status', 'created_at']);