先看慢查询日志再建索引,90%优化失败源于未定位真实慢SQL;需开启slow_query_log、设long_query_time=0.1s、log_queries_not_using_indexes=ON,用mysqldumpslow分析日志;再对慢SQL执行EXPLAIN,关注type、key、rows、Extra字段判断索引使用情况;联合索引按等值条件优先、范围条件置右设计;善用覆盖索引减少回表。

先看慢查询日志,别急着建索引
90% 的索引优化失败,是因为没搞清“到底哪条 SQL 慢”。盲目加索引不仅无效,还拖慢写入。必须先开启慢查询日志,让 MySQL 告诉你真实瓶颈在哪里。
-
SET GLOBAL slow_query_log = ON临时启用(重启失效) -
SET GLOBAL long_query_time = 0.1把阈值设为 100ms,覆盖核心业务响应要求 -
SET GLOBAL log_queries_not_using_indexes = ON记录所有未走索引的查询,哪怕它很快——这是索引遗漏的信号灯
日志路径默认在 /var/lib/mysql/slow.log,用 mysqldumpslow -s t -t 10 slow.log 快速抓出最耗时的 Top 10 查询。记住:优化对象永远是具体 SQL,不是“这张表好像该加个索引”。
EXPLAIN 是你的第一双眼睛
拿到慢 SQL 后,第一件事不是改代码,而是加 EXPLAIN 看执行计划。它直接暴露 MySQL 是否用了索引、怎么用的、有没有回表或排序开销。
-
type字段是关键:出现ALL(全表扫描)或index(全索引扫描),基本等于没走有效索引 -
key为NULL?说明根本没命中任何索引,得检查字段类型、引号、函数包裹等常见失效点 -
rows数值远大于实际返回行数?可能是统计信息过期,执行ANALYZE TABLE table_name更新一下 -
Extra出现Using filesort或Using temporary?说明ORDER BY或GROUP BY没利用上索引顺序,得考虑调整索引字段顺序或补充覆盖字段
例如 SELECT * FROM order WHERE user_id = 100 AND create_time > '2025-01-01',如果 EXPLAIN 显示 type=range 但 key=NULL,问题大概率出在 create_time 上用了函数(如 DATE(create_time)),导致索引失效。
联合索引设计:按查询条件顺序排,别把高区分度字段硬塞最左
联合索引不是“把 WHERE 里所有字段堆一起”,顺序决定它能服务哪些查询。核心是匹配业务中最常出现的条件组合,而不是字段本身的唯一性高低。
- 等值条件(
=、IN)优先放左边,比如WHERE status = 1 AND category_id = 5 AND created_at > '2026-01-01',应建(status, category_id, created_at),而非颠倒 - 范围条件(
>、<、BETWEEN)放最后,因为一旦出现,其右侧字段无法再用于索引查找(B+树只能顺序遍历到范围边界) - 避免只为单字段查询建联合索引:如果
category_id单独查很频繁,而status+category_id组合查较少,那更合理的方案是分别建INDEX(category_id)和INDEX(status, category_id),而非只建后者
一个典型反例:CREATE INDEX idx_user_time ON order (user_id, create_time) 看似合理,但如果业务中大量存在 WHERE create_time BETWEEN ... 单独查询,这个索引对它完全无效——create_time 不是最左列。
覆盖索引不是“锦上添花”,而是减少 IO 的刚需
只要查询字段能全部从索引中拿到,MySQL 就不用回表查聚簇索引里的完整行数据。这对大表、高并发场景是实打实的性能提升,尤其当 SELECT * 或含 TEXT/BLOB 字段时。
- 判断是否覆盖:看
EXPLAIN的Extra是否出现Using index - 设计技巧:把
SELECT中的非主键字段,追加到联合索引末尾。例如常用SELECT id, name, age FROM user WHERE city = 'Beijing',可建INDEX(city, name, age),即使city是筛选字段,name和age是取值字段 - 注意代价:索引越宽,占用空间越大,更新越慢。不要无脑把所有可能用到的字段都塞进索引——只加真正高频、低体积、且能消除回表的字段
最容易被忽略的一点:主键字段天然包含在所有二级索引中(InnoDB 存的是主键值),所以 SELECT id FROM table WHERE xxx 几乎总能走覆盖索引;但如果你查的是 SELECT id, username,而索引只有 (status),那就一定回表——哪怕你只多要了一个字段。










