EXPLAIN SELECT order_id, user_id, order_amount, status, created_at FROM orders WHERE status ='PENDING' AND order_amount BETWEEN500AND2000 ORDERBY created_at DESC LIMIT 50;
1 2 3 4 5
+----+-------------+--------+------+-----------------------------+-------------+---------+-------+---------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+-----------------------------+-------------+---------+-------+---------+-------------+ | 1 | SIMPLE | orders | ref | idx_status,idx_amount | idx_status | 1 | const | 1275000 | Using where | +----+-------------+--------+------+-----------------------------+-------------+---------+-------+---------+-------------+
EXPLAIN SELECT order_id, user_id, order_amount, status, created_at FROM orders WHERE status ='PENDING' AND order_amount BETWEEN500AND2000 ORDERBY created_at DESC LIMIT 50;
输出(关键列):
1 2 3 4 5
+----+-------------+--------+------+---------------------+------------+---------+-------+---------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------------+------------+---------+-------+---------+-------------+ | 1 | SIMPLE | orders | ref | idx_status,idx_amount | idx_status | 1 | const | 1200000 | Using where | +----+-------------+--------+------+---------------------+------------+---------+-------+---------+-------------+
-- 实际执行(强制走 idx_amount 对比) SELECT order_id, user_id, order_amount, status, created_at FROM orders FORCE INDEX (idx_amount) WHERE status ='PENDING' AND order_amount BETWEEN500AND2000 ORDERBY created_at DESC LIMIT 50;
-- 实际执行(走 idx_status,当前优化器选择) SELECT order_id, user_id, order_amount, status, created_at FROM orders WHERE status ='PENDING' AND order_amount BETWEEN500AND2000 ORDERBY created_at DESC LIMIT 50;
执行方式
扫描行数(EXPLAIN ANALYZE 实测)
执行时间
idx_status(优化器错误选择)
~2.1 万行(status 过滤后)再回表 + filesort
184 ms
idx_amount(正确索引)
~270 行
0.8 ms
差距约 230 倍。
步骤 3:创建直方图
针对 status 列(低基数列,自动用 Singleton)和 order_amount 列(高基数列,自动用 Equi-height)分别创建直方图:
1 2 3 4 5 6 7 8 9
-- 为 status 列创建直方图(自动选择 Singleton) ANALYZE TABLE orders UPDATE HISTOGRAM ON status WITH100 BUCKETS;
SELECT schema_name, table_name, column_name, JSON_PRETTY(histogram) AS histogram_json FROM information_schema.column_statistics WHERE schema_name ='shop' AND table_name ='orders'\G
EXPLAIN SELECT order_id, user_id, order_amount, status, created_at FROM orders WHERE status ='PENDING' AND order_amount BETWEEN500AND2000 ORDERBY created_at DESC LIMIT 50;
输出(关键列):
1 2 3 4 5
+----+-------------+--------+-------+---------------------+------------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------------+------------+---------+------+------+-------------+ | 1 | SIMPLE | orders | range | idx_status,idx_amount | idx_amount | 4 | NULL | 270 | Using where | +----+-------------+--------+-------+---------------------+------------+---------+------+------+-------------+
优化器现在准确预估 order_amount BETWEEN 500 AND 2000 匹配 270 行,并选择了 idx_amount 索引。执行时间从 184ms 降到 0.8ms,和被迫手动 FORCE INDEX 的效果一致,但这次是优化器自己“想明白了”。
步骤 6:验证性能提升
用 EXPLAIN ANALYZE 获得执行的真实行数和时间:
1 2 3 4 5 6 7
EXPLAIN ANALYZE SELECT order_id, user_id, order_amount, status, created_at FROM orders WHERE status ='PENDING' AND order_amount BETWEEN500AND2000 ORDERBY created_at DESC LIMIT 50;
输出关键信息:
1 2 3
-> Limit: 50 row(s) (actual time=0.721..0.735 rows=50 loops=1) -> Sort: orders.created_at DESC, limit input to 50 row(s) per chunk (actual time=0.719..0.731 rows=50 loops=1) -> Index range scan on orders using idx_amount over (500 <= order_amount <= 2000), with index condition: (orders.`status` = 'PENDING') (actual time=0.105..0.583 rows=270 loops=1)
-- 查看所有直方图 SELECT schema_name, table_name, column_name, JSON_EXTRACT(histogram, '$.histogram-type') AS hist_type, JSON_EXTRACT(histogram, '$.last-updated') AS last_updated FROM information_schema.column_statistics WHERE schema_name ='shop';
-- 删除指定列的直方图 ANALYZE TABLE orders DROP HISTOGRAM ON status;
-- 删除指定表所有列的直方图 ANALYZE TABLE orders DROP HISTOGRAM ONALL COLUMNS;
排查清单:执行计划走偏时怎么办
遇到优化器选错索引的情况,可以按以下顺序排查:
步骤
操作
目的
1
ANALYZE TABLE t;
刷新基础统计信息
2
SHOW INDEX FROM t; 检查 Cardinality
确认索引基数是否合理
3
SELECT * FROM information_schema.column_statistics WHERE table_name='t';