MySQL 查询优化不能只问“有没有索引”。一条 SQL 慢,可能和返回列、JOIN、过滤条件、排序、分页、回表和数据分布都有关系。索引只是其中一环,真正要看查询路径。
新增索引前先看查询形态
建索引前先判断 SQL 的主要形态:
- 等值查询。
- 范围查询。
- 排序。
- 分页。
- JOIN。
- 聚合。
- 覆盖索引。
不要只为单条 SQL 建孤立索引。更稳的做法是先盘点同一张表的高频查询,优先扩展已有联合索引,避免索引数量失控。
大表加索引要估算时间和风险
千万级甚至亿级表加索引,耗时可能从几分钟到数小时不等,取决于:
- 表数据量。
- 索引字段大小。
- 机器 IO。
- 云数据库规格。
- 是否在线 DDL。
- 当前业务写入压力。
比如给手机号字段增加普通索引:
alter table t_coupon add index idx_member_mobile(member_mobile);
执行前要确认业务低峰期、备份、锁表风险和回滚方案。
ORDER BY 不只是排序字段有没有索引
ORDER BY 的性能取决于过滤条件、排序字段和返回数据量。常见情况:
- 过滤条件能缩小很多数据,排序压力小。
- 排序字段有索引,但过滤后仍需要回表或文件排序。
- 联合索引顺序不匹配,导致索引用不上排序。
- 返回列太宽,导致 IO 成本高。
优化时先用 EXPLAIN 看访问类型、rows、key 和 extra。不要只看到排序字段有索引就认为一定快。
ON 和 WHERE 的位置会影响语义
JOIN 条件放在 ON 或 WHERE 后,语义可能不同,尤其是外连接。
示例:
select *
from orders o
left join users u on o.user_id = u.id and u.status = 1;
这里会保留左表记录,只是右表只连接符合状态的用户。
如果写成:
select *
from orders o
left join users u on o.user_id = u.id
where u.status = 1;
WHERE 会在连接结果之后过滤,可能把没有匹配右表的记录过滤掉,使 LEFT JOIN 更接近 INNER JOIN。
所以优化前先确认语义,不要为了“看起来更快”改变结果集。
冗余字段要看读写比例
冗余字段可以减少 JOIN,但不是银弹。判断时看:
- 查询是否高频。
- 更新是否低频。
- 字段一致性是否可维护。
- 是否能接受异步同步。
- 是否有校验和修复脚本。
读多写少、字段变化低频的场景,冗余字段有价值;写多读少或一致性要求极高的场景,冗余字段可能反而制造维护成本。
深分页要换思路
大 offset 分页慢,不是简单加索引就能解决。常见优化:
- 延迟关联:先查主键,再回表查详情。
- 游标分页:用上一次的排序键继续查。
- 限制最大页数。
- 改成搜索或筛选驱动。
分页越深,数据库越像在做大量丢弃工作。能改交互时,优先改交互。
实用结论
MySQL 索引优化的核心是查询路径。先用 EXPLAIN 看执行计划,再看过滤、排序、回表、JOIN 和分页。新增索引要服务一组查询,而不是单条 SQL;ON 和 WHERE 要先保证语义正确,再谈性能。




