====== Index Tuning ======
Index Tuning is the process of:
creating the right indexes
removing unnecessary indexes
optimizing query performance
Goal:
make queries faster
reduce full table scans
reduce CPU and disk I/O
improve application response time
===== What is an Index? =====
An index works like a book index.
Without index:
Need to scan every row
With index:
Jump directly to matching rows
===== Example Without Index =====
Table:
users ------------------- id email name
Query:
SELECT * FROM users WHERE email = 'a@gmail.com';
Without index:
MySQL scans ALL rows
This is called:
Full Table Scan
Slow when table becomes large.
===== Example With Index =====
Create index:
CREATE INDEX idx_users_email ON users(email);
Now MySQL can:
directly locate matching rows
instead of scanning entire table.
=> much faster
====== B-Tree Index ======
===== What is it? =====
Default MySQL index structure.
Used for:
equality search
range search
sorting
prefix matching
===== Example =====
CREATE INDEX idx_users_email ON users(email);
Fast queries:
WHERE email = 'a@gmail.com' WHERE email > 'm@gmail.com' ORDER BY email
===== Internally =====
B-Tree stores data in sorted order:
A C F H M Z
MySQL can quickly navigate tree branches.
Complexity:
O(log n)
instead of:
O(n)
====== Composite Index ======
===== What is it? =====
Index with multiple columns.
Example:
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
===== Why Useful? =====
Query:
SELECT * FROM orders WHERE user_id = 10 AND status = 'PAID';
Without composite index:
scan many rows
With composite index:
direct lookup
===== Leftmost Prefix Rule =====
Composite index:
(user_id, status, created_at)
Works for:
WHERE user_id = ? WHERE user_id = ? AND status = ? WHERE user_id = ? AND status = ? AND created_at > ?
Does NOT work efficiently for:
WHERE status = 'PAID'
because user_id is missing.
====== FULLTEXT Index ======
===== What is it? =====
Used for text searching.
Example:
CREATE FULLTEXT INDEX idx_articles_content ON articles(content);
===== Example Query =====
SELECT * FROM articles WHERE MATCH(content) AGAINST('mysql optimization');
Useful for:
search engine
blog search
article search
===== Difference from LIKE =====
Using LIKE:
WHERE content LIKE '%mysql%'
Usually causes:
full table scan
FULLTEXT is much faster for large text datasets.
====== EXPLAIN ======
===== What is it? =====
Shows how MySQL executes query.
Most important tool for index tuning.
===== Example =====
EXPLAIN SELECT * FROM users WHERE email = 'a@gmail.com';
===== Important Fields =====
^ Field ^ Meaning ^
| type | query efficiency |
| key | index being used |
| rows | estimated scanned rows |
| Extra | additional operations |
===== Good Query =====
type = ref rows = 1 key = idx_users_email
Meaning:
using index
scanning very few rows
===== Bad Query =====
type = ALL key = NULL rows = 1000000
Meaning:
full table scan
no index used
Very slow.
====== Common Index Problems ======
===== Too Many Indexes =====
Every INSERT/UPDATE/DELETE must also update indexes.
Too many indexes cause:
slower writes
more disk usage
higher memory usage
===== Index Not Used =====
Example:
WHERE YEAR(created_at) = 2025
Problem:
function on indexed column
MySQL may ignore index.
Better:
WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01'
===== Leading Wildcard =====
Bad:
WHERE email LIKE '%gmail.com'
Cannot efficiently use B-Tree index.
Better:
WHERE email LIKE 'abc%'
====== Query Optimization Tips ======
===== Select Only Needed Columns =====
Bad:
SELECT * FROM users;
Better:
SELECT id, name FROM users;
===== Avoid N+1 Queries =====
Bad:
1 query for users 1000 queries for orders
Better:
JOIN users and orders
===== Use Proper Index =====
Common indexing strategy:
^ Query Pattern ^ Recommended Index ^
| WHERE email = ? | (email) |
| WHERE user_id = ? AND status = ? | (user_id, status) |
| ORDER BY created_at | (created_at) |
| WHERE created_at BETWEEN | (created_at) |
====== Production Example ======
Table:
orders ( id, user_id, status, created_at )
Common query:
SELECT * FROM orders WHERE user_id = 100 AND status = 'PAID' ORDER BY created_at DESC LIMIT 20;
Recommended index:
CREATE INDEX idx_orders_user_status_created ON orders(user_id, status, created_at);
Why:
filter by user_id
filter by status
sort by created_at
=> avoids full scan and extra sorting.
====== Summary ======
^ Concept ^ Purpose ^
| B-Tree Index | Fast lookup and sorting |
| Composite Index | Optimize multi-column queries |
| FULLTEXT Index | Fast text searching |
| EXPLAIN | Analyze query execution |
| Index Tuning | Improve query performance |