用 EXPLAIN
看清查询到底在扫多少行
不看执行计划就调优,等于蒙眼修车。对大表查询,第一件事是加
EXPLAIN前缀跑一遍,重点盯
rows和
type字段:
-
rows显示预估扫描行数,如果远大于结果集行数,说明索引没生效或用了低效索引
-
type是
ALL(全表扫描)或
index(全索引扫描),基本等于性能红灯
-
key为空,或
key_len远小于预期,常因查询条件没用上联合索引最左前缀
联合索引设计必须匹配查询模式
大表上建单列索引效果有限,关键在让索引覆盖查询条件 + 排序 + 返回字段:
- 条件中有
WHERE a = ? AND b > ? ORDER BY c,索引应为
(a, b, c),顺序不能错
- 如果还要
SELECT d, e,考虑加
INCLUDE(MySQL 8.0.13+ 支持)或直接建覆盖索引
(a, b, c, d, e)
- 避免在索引列上做函数操作,比如
WHERE YEAR(created_at) = 2023会让索引失效,改用
created_at BETWEEN '2023-01-01' AND '2023-12-31'
分页深翻时别用 LIMIT offset, size
当
offset超过几十万,MySQL 仍要从头扫描并丢弃前面所有行,I/O 和 CPU 开销陡增:
- 改用游标分页:记录上一页最后一条的主键值,下一页查
WHERE id > last_id ORDER BY id LIMIT 100
- 若业务允许,用
WHERE create_time > ? ORDER BY create_time LIMIT 100替代基于偏移的分页
- 禁止在管理后台写
LIMIT 1000000, 20—— 这类语句应加权限拦截或走异步导出
读多写少场景可考虑物化视图替代实时聚合
对大表频繁执行
GROUP BY + COUNT/SUM,即使加了索引,每次查询仍要遍历大量数据:
- MySQL 本身不支持物化视图,但可用定时任务把聚合结果写入汇总表,比如每小时跑一次
INSERT INTO summary_daily SELECT date(created_at), COUNT(*) FROM big_table GROUP BY date(created_at)
- 查询时直接查汇总表,响应时间从秒级降到毫秒级
- 注意汇总表和源表的数据一致性,避免用
REPLACE INTO或
ON DUPLICATE KEY UPDATE时漏更新
实际优化中,最常被忽略的是查询与索引的“严丝合缝”——差一个字段顺序、多一个函数包装、少一个
ORDER BY字段,索引就形同虚设。大表不是靠堆硬件扛住的,是靠让每一行扫描都算得上。
