90%的生产慢查询源于索引未被正确使用或sql破坏索引有效性;需先用explain确认type=all或key=null原因,再检查函数操作、隐式转换、联合索引最左前缀是否满足,而非盲目加索引。

直接说结论:90%的生产慢查询,不是缺索引,而是索引没建对、没用上,或者SQL本身在破坏索引有效性。
EXPLAIN 显示 type=ALL 或 key=NULL 怎么办
这是最典型的“没走索引”信号,说明MySQL正在全表扫描。别急着加索引,先确认三件事:
- WHERE 条件里有没有对索引字段做函数操作?比如
WHERE YEAR(create_time) = 2024—— 这会让所有索引失效,改成WHERE create_time >= '2024-01-01' AND create_time - 字段类型和查询值类型是否一致?比如
user_id是VARCHAR,但你写了WHERE user_id = 123(数字),会触发隐式转换,索引失效 - 联合索引的最左前缀是否被满足?比如有
INDEX idx_status_time (status, create_time),但查询只写了WHERE create_time > '2024-01-01'—— status 没出现在条件里,这个索引就完全用不上
ORDER BY + LIMIT 深分页(OFFSET 10000)怎么优化
这类查询常见于后台列表页,ORDER BY create_time DESC LIMIT 20 OFFSET 10000 看似简单,实际要先扫完前10020行再取20条,效率极低。
- 优先改成分页游标(cursor-based pagination):用上一页最后一条的
create_time和id作为下一页起点,例如WHERE create_time - 如果必须用 OFFSET,确保
ORDER BY字段在 WHERE 条件中已有等值过滤,并且该字段已包含在联合索引最右侧。例如WHERE status = 1 ORDER BY create_time DESC,索引应为INDEX idx_status_time (status, create_time),不能反过来 - 避免在大表上用
SELECT *配合深分页 —— 扫描+回表成本太高,考虑只查主键,再用主键二次查详情
ALTER TABLE ADD INDEX 会不会锁表
会,但取决于 MySQL 版本和存储引擎:
- MySQL 5.6+ 的 InnoDB 支持
ALGORITHM=INPLACE(默认),加普通索引通常只锁写不锁读,线上可操作;但加主键或唯一索引仍可能全程锁表 - MySQL 8.0+ 支持
ALGORITHM=INSTANT,仅修改元数据,秒级完成,但仅限新增列、重命名列、添加/删除二级索引(不含全文索引) - 执行前务必确认:运行
SHOW CREATE TABLE orders查看当前引擎;用SELECT VERSION()确认版本;生产环境加索引必须避开高峰期,并提前在从库验证执行时间 - 别信“加索引一定快”——索引越多,INSERT/UPDATE/DELETE 越慢,尤其是高频写入表,每个索引都要维护B+树。一个表超过5个二级索引就要警惕
覆盖索引能省掉回表,但哪些字段该放进索引
覆盖索引本质是让查询全部字段都落在索引里,不用再回到聚簇索引查数据行。但它不是“把所有WHERE+SELECT字段都塞进联合索引”。
- 优先放进索引的字段顺序:等值查询字段(最左)→ 范围查询字段 →
ORDER BY字段 → 最后才是SELECT中需要的非索引字段(仅限少量、固定、小体积字段如status,is_deleted) - 大字段(
TEXT,BLOB, 长VARCHAR)绝不能放进索引 —— 会撑爆索引页,反而拖慢查询 - 示例:查询
SELECT id, title, status FROM articles WHERE category = 'tech' AND publish_time > '2024-01-01' ORDER BY publish_time DESC,理想索引是INDEX idx_cat_time (category, publish_time, status, id, title)—— 注意title放最后,且前提是它不太长;否则只放id和status,靠主键回表取title
真正卡住人的,往往不是“要不要加索引”,而是 WHERE 条件写法、字段类型隐式转换、联合索引字段顺序这些细节。上线前拿 EXPLAIN 对着每条慢 SQL 跑一遍,比盲目加十个索引更管用。











