====== 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 |