explain是大表查询调优首要步骤,重点看rows(预估扫描行数)和type(all/index为性能红灯),确保索引覆盖查询条件、排序及返回字段,避免函数操作导致失效,深分页改用游标,聚合查询用汇总表替代。

用 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 字段,索引就形同虚设。大表不是靠堆硬件扛住的,是靠让每一行扫描都算得上。