MySQL 索引指南
创建索引
各种索引类型的基本创建语法。
-- Single-column index
CREATE INDEX idx_users_email ON users(email);
-- Unique index
CREATE UNIQUE INDEX idx_users_username ON users(username);
-- Composite index (column order matters!)
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
-- Full-text index
CREATE FULLTEXT INDEX idx_articles_body ON articles(title, body);
-- Prefix index (first 10 chars of a long column)
CREATE INDEX idx_url_prefix ON pages(url(10));
-- Drop index
DROP INDEX idx_users_email ON users;
-- Show indexes on a table
SHOW INDEX FROM users\G
复合索引与覆盖索引
使用最左前缀规则设计复合索引。覆盖索引可完全从索引中满足查询需求。
-- Composite index: (status, created_at)
-- Useful for: WHERE status = ? ORDER BY created_at
CREATE INDEX idx_orders_status_date ON orders(status, created_at);
-- Covering index: includes all columns needed by the query
CREATE INDEX idx_covering ON orders(user_id, status, total)
-- query: SELECT status, total FROM orders WHERE user_id = ?
-- no table lookup needed (Extra: Using index)
-- Leftmost prefix usage
-- Index: (a, b, c)
-- ✓ WHERE a = 1
-- ✓ WHERE a = 1 AND b = 2
-- ✓ WHERE a = 1 AND b = 2 AND c = 3
-- ✗ WHERE b = 2 (skips leading column)
-- ✗ WHERE b = 2 AND c = 3
-- Range break: stop using index after first range column
-- Index: (status, price, name)
-- ✓ WHERE status = 'A' AND price < 100 (uses status + price)
-- ✗ column 'name' not used after range on price
EXPLAIN 分析
使用 EXPLAIN 检查查询执行计划并识别缺失的索引。
-- Basic EXPLAIN
EXPLAIN SELECT * FROM orders WHERE user_id = 42;
-- EXPLAIN with FORMAT=JSON for more detail
EXPLAIN FORMAT=JSON
SELECT u.name, COUNT(o.id)
FROM users u JOIN orders o ON u.id = o.user_id
GROUP BY u.id\G
-- Key columns to watch:
-- type: ALL (full scan) → range → ref → eq_ref → const (best)
-- key: index actually used (NULL = none used)
-- rows: estimated rows examined
-- Extra: "Using filesort" or "Using temporary" = potential problem
-- EXPLAIN ANALYZE (MySQL 8.0+): runs the query and shows actual costs
EXPLAIN ANALYZE
SELECT * FROM products WHERE category_id = 5 ORDER BY price;
| type | 含义 |
|---|---|
ALL | 全表扫描——通常需要索引 |
index | 全索引扫描 |
range | 索引范围扫描(WHERE 含 <、>、BETWEEN) |
ref | 非唯一索引查找 |
eq_ref | 唯一索引查找(每次连接一行) |
const | 主键/唯一键查找——最快 |
索引提示
当优化器做出不佳选择时,强制、建议或忽略特定索引。
-- Force an index
SELECT * FROM orders FORCE INDEX (idx_orders_user_date)
WHERE user_id = 42;
-- Suggest an index (optimizer may still ignore)
SELECT * FROM orders USE INDEX (idx_orders_status_date)
WHERE status = 'pending';
-- Ignore an index
SELECT * FROM orders IGNORE INDEX (idx_created_at)
WHERE created_at > '2024-01-01';
-- Force index for ORDER BY
SELECT * FROM products
FORCE INDEX FOR ORDER BY (idx_price)
ORDER BY price ASC LIMIT 10;
索引最佳实践
| 规则 | 说明 |
|---|---|
| 高基数列在前 | 复合索引中将选择性最高的列放在最前面 |
| 避免对索引列使用函数 | WHERE YEAR(created_at) = 2024 会绕过索引,改用范围条件 |
| 注意隐式类型转换 | WHERE varchar_col = 123 会导致全表扫描 |
| 限制索引数量 | 每个索引都会降低 INSERT/UPDATE/DELETE 速度 |
| 使用 pt-duplicate-key-checker | Percona 工具用于查找冗余索引 |