← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Database Indexes — Types & Trade-offs

database PHP 5.0+ Intermediate
debt(d5/e3/b7/t5)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches' (d5). The term's detection_hints list mysql-slow-query-log, pt-query-digest, and percona-advisor — all specialist tools that identify missing or problematic indexes via EXPLAIN analysis and slow query monitoring. Standard linters won't catch index issues; you need database-specific profiling tools.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix states adding an index on columns used in WHERE/JOIN/ORDER BY clauses. Adding or dropping a single index is typically a one-line DDL statement (e1), but proper index strategy often requires reviewing multiple queries and may involve composite indexes or removing unused ones — touching multiple migration files or affecting several queries, pushing toward e3.

b7 Burden Structural debt — long-term weight of choosing wrong

Closest to 'strong gravitational pull' (b7). Index design is a load-bearing architectural decision with wide reach — applies_to shows web, cli, and queue-worker contexts. Poor indexing strategy pervades the entire application's database interactions. Every new query pattern must consider existing indexes, every schema change must account for index maintenance costs. The decay flavor applies: wrong index choices slowly poison performance as data grows.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap' (t5). The misconception explicitly states 'More indexes always improve query performance' — a documented gotcha that most developers eventually learn but initially get wrong. The hidden write overhead and query planner confusion from over-indexing contradicts the intuitive assumption that indexes are purely beneficial. This is a well-known trap but not catastrophic since the failure mode (slow writes, suboptimal plans) is observable.

About DEBT scoring →

Also Known As

database indexes SQL index types B-tree index

TL;DR

B-Tree, hash, full-text, and partial indexes — each suited to different query patterns, with write overhead as the cost of read speed.

Explanation

B-Tree indexes (default in MySQL/PostgreSQL): ordered structure supporting equality, range queries, ORDER BY, and prefix matching (LIKE 'foo%'). Hash indexes: O(1) equality lookups, no range support — PostgreSQL supports them; MySQL memory tables only. Full-text indexes: tokenise text for keyword search (MATCH AGAINST in MySQL, tsvector/GIN in PostgreSQL) — far better than LIKE '%keyword%'. Partial indexes (PostgreSQL): CREATE INDEX ON orders(created_at) WHERE status = 'pending' — indexes only the subset, dramatically reducing size and improving selectivity for filtered queries. Composite indexes: column order matters — put equality conditions first, range conditions last, match GROUP BY/ORDER BY columns. Every index slows INSERT/UPDATE/DELETE — add indexes for proven slow queries, not preemptively.

Diagram

flowchart TD
    subgraph B-Tree Index - Default
        BT[Ordered tree structure<br/>range queries O of log n<br/>equality O of log n]
    end
    subgraph Hash Index
        HI[Hash map structure<br/>equality only O of 1<br/>no range queries]
    end
    subgraph GIN Index - Full Text
        GIN2[Inverted index<br/>array contains<br/>JSONB keys<br/>full-text search]
    end
    subgraph Partial Index
        PI[Index with WHERE clause<br/>WHERE status=active<br/>smaller faster index]
    end
    QUERY[Query type] -->|"=, >, <, BETWEEN"| BT
    QUERY -->|"= only"| HI
    QUERY -->|"@>, ??, tsvector"| GIN2
    QUERY -->|"common filter"| PI
style BT fill:#238636,color:#fff
style PI fill:#238636,color:#fff
style GIN2 fill:#6e40c9,color:#fff

Common Misconception

More indexes always improve query performance. Each index adds write overhead — every INSERT, UPDATE, or DELETE must update all relevant indexes. Over-indexed tables can be slower to write than correctly indexed ones, and the query planner may choose a suboptimal index when too many exist.

Why It Matters

A missing index on a frequently-queried column turns a millisecond lookup into a full table scan that takes seconds as data grows. Indexes are the highest-leverage database performance tool — but over-indexing slows writes and wastes storage.

Common Mistakes

  • Adding indexes after performance problems appear instead of designing them with the query patterns.
  • Creating single-column indexes when composite indexes would serve multiple queries more efficiently.
  • Indexing low-cardinality columns (boolean, status with 3 values) — the optimiser may ignore them anyway.
  • Not removing unused indexes — every index adds overhead to INSERT, UPDATE, and DELETE operations.

Code Examples

✗ Vulnerable
-- Missing index on foreign key — full table scan on every JOIN:
SELECT o.id, u.email FROM orders o JOIN users u ON o.user_id = u.id;
-- No index on orders.user_id — scans entire orders table per user

-- Add index:
CREATE INDEX idx_orders_user_id ON orders (user_id);
-- EXPLAIN now shows: Index Scan using idx_orders_user_id
✓ Fixed
-- B-Tree (default) — equality, range, ORDER BY
CREATE INDEX idx_orders_user    ON orders(user_id);
CREATE INDEX idx_orders_created ON orders(created_at DESC);

-- Composite — equality columns first, range/sort last
CREATE INDEX idx_orders_user_status ON orders(user_id, status, created_at);

-- Partial (PostgreSQL) — only index the interesting subset
CREATE INDEX idx_orders_unpaid ON orders(created_at)
    WHERE status IN ('pending', 'processing');

-- Covering — all query columns in the index, no heap fetch
CREATE INDEX idx_orders_cover ON orders(user_id) INCLUDE (total, status);

-- Full-text (PostgreSQL)
CREATE INDEX idx_products_fts ON products USING GIN(to_tsvector('english', name || ' ' || description));

-- Verify index is used
EXPLAIN (ANALYZE) SELECT total FROM orders WHERE user_id = 42;

Added 15 Mar 2026
Edited 19 Apr 2026
Views 26
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 1 ping S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S
No pings yet today
Perplexity 8 Amazonbot 7 Google 2 Unknown AI 2 Ahrefs 2
crawler 20 crawler_json 1
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Medium
⚡ Quick Fix
Add an index on every column used in WHERE, JOIN ON, or ORDER BY clauses — but avoid over-indexing write-heavy tables where each insert/update pays the index maintenance cost
📦 Applies To
PHP 5.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
EXPLAIN type:ALL on production query; WHERE clause on unindexed column; JOIN on unindexed column causing full scan
Auto-detectable: ✓ Yes mysql-slow-query-log pt-query-digest percona-advisor
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: File

✓ schema.org compliant