-- 完全命中:使用索引 SELECT*FROM orders WHERE user_id =1AND status =1AND create_time >'2024-01-01';
-- 命中前两个:使用索引 SELECT*FROM orders WHERE user_id =1AND status =1;
-- 命中第一个:使用索引 SELECT*FROM orders WHERE user_id =1;
-- 跳过第一个:不使用索引(索引断裂) SELECT*FROM orders WHERE status =1; SELECT*FROM orders WHERE create_time >'2024-01-01'; SELECT*FROM orders WHERE user_id =1AND create_time >'2024-01-01';
-- 如果 status = 0/1 各占 50%,单独用 status 没意义 -- 但 user_id + status 组合效果好
索引重建与优化
查看现有索引
1 2 3 4 5
-- 查看表的所有索引 SHOW INDEX FROM orders;
-- 查看某个索引的使用情况 SHOW INDEX FROM orders FROM mysql;
重建索引
1 2 3 4 5
-- 优化表(重建索引) OPTIMIZE TABLE orders;
-- 或 ALTERTABLE orders ENGINE = InnoDB;
删除冗余索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-- 查找冗余索引 SELECT a.index_name as `Index`, a.column_name asColumn, b.index_name as RedundantTo, b.column_name as RedundantColumn FROM information_schema.statistics a JOIN information_schema.statistics b ON a.table_name = b.table_name AND a.index_name != b.index_name AND a.column_name = b.column_name WHERE a.index_name !='PRIMARY';
-- 删除冗余索引 ALTERTABLE orders DROP INDEX idx_redundant;
添加索引(在线 DDL)
1 2 3 4 5
-- MySQL 5.6+ 支持在线 DDL ALTERTABLE orders ADD INDEX idx_user_status (user_id, status), ALGORITHM=INPLACE, -- 在线修改 LOCK=NONE; -- 不锁表
实战:复杂查询优化
原始 SQL(2秒)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
SELECT u.id, u.name, COUNT(o.id) as order_count, SUM(o.amount) as total_amount, AVG(o.amount) as avg_amount FROM users u LEFTJOIN orders o ON u.id = o.user_id LEFTJOIN order_items oi ON o.id = oi.order_id LEFTJOIN products p ON oi.product_id = p.id WHERE u.status =1 AND o.create_time >='2024-01-01' AND o.create_time <'2025-01-01' AND p.category_id IN (1, 2, 3, 4, 5) GROUPBY u.id, u.name HAVINGCOUNT(o.id) >10 ORDERBY total_amount DESC LIMIT 100;
EXPLAIN 分析
1 2 3 4 5 6 7
EXPLAIN SELECT ...
-- 问题发现: -- type: ALL (全表扫描) -- key: NULL (没有索引) -- rows: 500000 (扫描 50 万行) -- Extra: Using filesort, Using temporary
-- Step 1: 添加必要的索引 ALTERTABLE orders ADD INDEX idx_status_time (status, create_time); ALTERTABLE order_items ADD INDEX idx_order_id (order_id); ALTERTABLE products ADD INDEX idx_category (category_id);
-- Step 2: 优化 SQL 结构 SELECT u.id, u.name, order_stats.order_count, order_stats.total_amount, order_stats.avg_amount FROM users u INNERJOIN ( -- 预聚合,减少数据量 SELECT o.user_id, COUNT(*) as order_count, SUM(o.amount) as total_amount, AVG(o.amount) as avg_amount FROM orders o INNERJOIN order_items oi ON o.id = oi.order_id INNERJOIN products p ON oi.product_id = p.id WHERE o.status =1 AND o.create_time >='2024-01-01' AND o.create_time <'2025-01-01' AND p.category_id IN (1, 2, 3, 4, 5) GROUPBY o.user_id HAVINGCOUNT(*) >10 ) order_stats ON u.id = order_stats.user_id WHERE u.status =1 ORDERBY order_stats.total_amount DESC LIMIT 100;
-- Step 3: 再次 EXPLAIN EXPLAIN SELECT ...
优化后效果
1 2 3 4
-- type: ref (使用索引) -- key: idx_status_time (使用索引) -- rows: 1000 (只扫描 1000 行) -- Extra: Using index (覆盖索引)