你是否有过这种经历数据量才几十万一条查询语句却要跑好几秒用户已经点了三次刷新你盯着屏幕满头大汗。问题大概率出在索引上。索引就像书的目录没有它数据库就得逐页翻找这叫全表扫描。今天我们不谈高深理论就聊实战中怎么建索引、怎么排查问题让你的SQL从“步行”变成“飞行”。第一步先把“慢”找出来排查慢查询第一件事是打开记录开关。MySQL有个配置叫 slow_query_log把它设为ON再设置 long_query_time 为1秒任何执行超过1秒的SQL都会被记录到慢查询日志里。但光有日志不够真正好用的工具是 mysqldump 之外的另一个神器pt-query-digest来自Percona Toolkit工具集。它能把慢查询日志分析得明明白白按执行次数、总耗时、锁等待时间排序一眼就能看出哪些SQL是拖垮系统的元凶。在服务器上跑一条命令pt-query-digest /var/lib/mysql/slow-query.log它会生成一份报告排在顶部的SQL就是你需要优先处理的。记住一个原则优化总耗时最大的SQL而不是单次最慢的。一条执行100万次每次0.1秒的SQL对系统的影响远大于一条执行10次每次5秒的SQL。第二步看懂执行计划别被假索引骗了找到目标SQL后用EXPLAIN分析它的执行计划。在SQL前面加上EXPLAIN关键字MySQL会告诉你它打算怎么执行这条查询而不是真正去执行。EXPLAIN SELECT * FROM orders WHERE user_id 10086 AND status paid ORDER BY created_at DESC LIMIT 10;结果里有几个关键字段必须读懂。type列表示访问类型从好到差依次是const常量查找、eq_ref唯一索引关联、ref非唯一索引查找、range范围扫描、index索引全扫描、ALL全表扫描。如果你的type是ALL那就好比你在图书馆里逐本翻书找资料效率可想而知。possible_keys是“可能用到的索引”keys是“实际用到的索引”。如果possible_keys有值但keys是NULL说明优化器认为不走索引更快——这通常意味着你的索引建错了或者查询条件导致索引失效。最容易被坑的是Extra列。如果看到Using filesort说明排序没有用到索引MySQL在内存或磁盘上额外做了一次排序数据量大时这是性能杀手。看到Using temporary就更糟了说明需要临时表来存中间结果。这两兄弟只要出现就说明你的索引没覆盖到位。第三步索引失效的六个经典场景索引建了不代表就会用下面的场景会让索引直接失灵。对索引列做函数操作。WHERE DATE(create_time) 2026-05-24 会让 create_time 上的索引完全失效。正确做法是用范围查询WHERE create_time 2026-05-24 00:00:00 AND create_time 2026-05-25 00:00:00。隐式类型转换。如果 phone 字段是 VARCHAR 类型你写 WHERE phone 13800138000MySQL会把字符串列全部转成数字去比较索引作废。一定要写成 WHERE phone 13800138000。前置模糊查询。WHERE title LIKE %MySQL优化% 中百分号在前面B树的有序性完全用不上。但后置模糊 LIKE MySQL优化% 是可以走索引的。如果需要全文检索就用MySQL内置的全文索引或外接Elasticsearch。不等于和NOT IN。WHERE status ! deleted 通常不会走索引因为优化器觉得排除一个值可能还要扫大部分数据。这种场景考虑用IN枚举出需要的状态值。联合索引不满足最左前缀原则。你建了INDEX idx_a_b_c (a, b, c)查询条件是 WHERE b1 AND c2没有用到a字段这个联合索引相当于没建。联合索引要从最左边的列开始匹配跳过了a后面的都用不上。索引列参与计算。WHERE price * discount 100 不会走索引改成 WHERE price 100 / discount 就行了把计算挪到等号右边。第四步建立正确的索引策略知道索引怎么失灵之后建索引就有章法了。核心原则是为高频查询创建覆盖索引。覆盖索引的意思是查询需要的所有字段都包含在索引里MySQL扫描索引就能拿到全部数据不用回表查主键索引。用EXPLAIN看Extra列出现Using index就说明走了覆盖索引。联合索引的字段顺序有讲究。把区分度高的、查询条件中必有的字段放在前面。比如查询订单最常见的就是WHERE user_id ? AND status ?那就建 INDEX idx_user_status (user_id, status)而不是反过来。还有一个容易被忽略的操作定期分析和优化索引。表数据频繁增删改之后索引碎片会增多。定期执行 ANALYZE TABLE 更新统计信息让优化器做出更准确的判断。对于碎片严重的表OPTIMIZE TABLE 可以重建表结构和索引不过执行时会锁表记得选在低峰期。最后说一句索引不是越多越好。每增加一个索引写入操作就要多维护一棵B树空间占用也相应增长。不要给每个查询都建索引而是观察慢查询日志为真正高频且慢的查询建立合适索引同时定期清理无用索引。MySQL 8.0可以查询 sys.schema_unused_indexes 视图找出从未被使用的索引果断删掉。MySQL优化没有一劳永逸的诀窍它是伴随业务增长持续调优的过程。但掌握上述方法之后你就不再是盲目建索引而是有章法地排查和解决问题。下次再看到慢查询报警你不会慌张只会冷静地打开EXPLAIN开始破案。
别让你的SQL在数据库里“迷路”——MySQL索引优化与慢查询排查实战指南
你是否有过这种经历数据量才几十万一条查询语句却要跑好几秒用户已经点了三次刷新你盯着屏幕满头大汗。问题大概率出在索引上。索引就像书的目录没有它数据库就得逐页翻找这叫全表扫描。今天我们不谈高深理论就聊实战中怎么建索引、怎么排查问题让你的SQL从“步行”变成“飞行”。第一步先把“慢”找出来排查慢查询第一件事是打开记录开关。MySQL有个配置叫 slow_query_log把它设为ON再设置 long_query_time 为1秒任何执行超过1秒的SQL都会被记录到慢查询日志里。但光有日志不够真正好用的工具是 mysqldump 之外的另一个神器pt-query-digest来自Percona Toolkit工具集。它能把慢查询日志分析得明明白白按执行次数、总耗时、锁等待时间排序一眼就能看出哪些SQL是拖垮系统的元凶。在服务器上跑一条命令pt-query-digest /var/lib/mysql/slow-query.log它会生成一份报告排在顶部的SQL就是你需要优先处理的。记住一个原则优化总耗时最大的SQL而不是单次最慢的。一条执行100万次每次0.1秒的SQL对系统的影响远大于一条执行10次每次5秒的SQL。第二步看懂执行计划别被假索引骗了找到目标SQL后用EXPLAIN分析它的执行计划。在SQL前面加上EXPLAIN关键字MySQL会告诉你它打算怎么执行这条查询而不是真正去执行。EXPLAIN SELECT * FROM orders WHERE user_id 10086 AND status paid ORDER BY created_at DESC LIMIT 10;结果里有几个关键字段必须读懂。type列表示访问类型从好到差依次是const常量查找、eq_ref唯一索引关联、ref非唯一索引查找、range范围扫描、index索引全扫描、ALL全表扫描。如果你的type是ALL那就好比你在图书馆里逐本翻书找资料效率可想而知。possible_keys是“可能用到的索引”keys是“实际用到的索引”。如果possible_keys有值但keys是NULL说明优化器认为不走索引更快——这通常意味着你的索引建错了或者查询条件导致索引失效。最容易被坑的是Extra列。如果看到Using filesort说明排序没有用到索引MySQL在内存或磁盘上额外做了一次排序数据量大时这是性能杀手。看到Using temporary就更糟了说明需要临时表来存中间结果。这两兄弟只要出现就说明你的索引没覆盖到位。第三步索引失效的六个经典场景索引建了不代表就会用下面的场景会让索引直接失灵。对索引列做函数操作。WHERE DATE(create_time) 2026-05-24 会让 create_time 上的索引完全失效。正确做法是用范围查询WHERE create_time 2026-05-24 00:00:00 AND create_time 2026-05-25 00:00:00。隐式类型转换。如果 phone 字段是 VARCHAR 类型你写 WHERE phone 13800138000MySQL会把字符串列全部转成数字去比较索引作废。一定要写成 WHERE phone 13800138000。前置模糊查询。WHERE title LIKE %MySQL优化% 中百分号在前面B树的有序性完全用不上。但后置模糊 LIKE MySQL优化% 是可以走索引的。如果需要全文检索就用MySQL内置的全文索引或外接Elasticsearch。不等于和NOT IN。WHERE status ! deleted 通常不会走索引因为优化器觉得排除一个值可能还要扫大部分数据。这种场景考虑用IN枚举出需要的状态值。联合索引不满足最左前缀原则。你建了INDEX idx_a_b_c (a, b, c)查询条件是 WHERE b1 AND c2没有用到a字段这个联合索引相当于没建。联合索引要从最左边的列开始匹配跳过了a后面的都用不上。索引列参与计算。WHERE price * discount 100 不会走索引改成 WHERE price 100 / discount 就行了把计算挪到等号右边。第四步建立正确的索引策略知道索引怎么失灵之后建索引就有章法了。核心原则是为高频查询创建覆盖索引。覆盖索引的意思是查询需要的所有字段都包含在索引里MySQL扫描索引就能拿到全部数据不用回表查主键索引。用EXPLAIN看Extra列出现Using index就说明走了覆盖索引。联合索引的字段顺序有讲究。把区分度高的、查询条件中必有的字段放在前面。比如查询订单最常见的就是WHERE user_id ? AND status ?那就建 INDEX idx_user_status (user_id, status)而不是反过来。还有一个容易被忽略的操作定期分析和优化索引。表数据频繁增删改之后索引碎片会增多。定期执行 ANALYZE TABLE 更新统计信息让优化器做出更准确的判断。对于碎片严重的表OPTIMIZE TABLE 可以重建表结构和索引不过执行时会锁表记得选在低峰期。最后说一句索引不是越多越好。每增加一个索引写入操作就要多维护一棵B树空间占用也相应增长。不要给每个查询都建索引而是观察慢查询日志为真正高频且慢的查询建立合适索引同时定期清理无用索引。MySQL 8.0可以查询 sys.schema_unused_indexes 视图找出从未被使用的索引果断删掉。MySQL优化没有一劳永逸的诀窍它是伴随业务增长持续调优的过程。但掌握上述方法之后你就不再是盲目建索引而是有章法地排查和解决问题。下次再看到慢查询报警你不会慌张只会冷静地打开EXPLAIN开始破案。