mysql如何优化SQL语句_mysql查询优化实战方法

1次阅读

索引非万能,函数、隐式转换、or等致失效;order by不匹配索引触发filesort;join缺索引易笛卡尔积;线上加索引需algorithm=inplace或pt-osc避锁表。

mysql如何优化SQL语句_mysql查询优化实战方法

为什么加了索引查询还是慢

索引不是万能的,WHERE 条件中用了函数、类型隐式转换、或 OR 连接多个非索引字段,都会导致索引失效。比如 WHERE YEAR(create_time) = 2023mysql 无法用 create_time 上的索引;又如 WHERE user_id = '123'user_idint),会触发隐式转换,全表扫描。

实操建议:

  • EXPLAINtype 是否为 ref/range,避免 ALL;关注 keypossible_keys 是否命中预期索引
  • 把函数移到等号右侧:把 YEAR(create_time) = 2023 改成 create_time BETWEEN '2023-01-01' AND '2023-12-31'
  • 确保比较字段类型一致:user_id = 123(而非字符串 '123'
  • 多个 OR 条件尽量改写为 union,尤其当各分支可走不同索引时

ORDER BY 为什么会触发 filesort

ORDER BY 字段不在索引最左前缀,或索引包含 WHERE 条件但顺序不匹配时,MySQL 就得额外排序,即 Extra: using filesort。这不是真写磁盘文件,但意味着不能利用索引有序性,性能下降明显。

实操建议:

  • 复合索引要兼顾 WHEREORDER BY:例如查询 select * FROM order WHERE status = 'paid' ORDER BY created_at DESC,建索引应为 (status, created_at),而非单独 created_at
  • 避免 SELECT * 配合大字段(如 TEXT),否则即使走了索引,回表代价也高;考虑覆盖索引:把常用查询字段加入索引末尾,如 (status, created_at, user_id, amount)
  • ORDER BY ... DESC 在 MySQL 8.0+ 支持降序索引,但 5.7 及以前默认升序,DESC 不生效,需确认版本

JOIN 多张表时怎么避免笛卡尔积和慢查询

没写 ON 条件、或 ON 字段无索引,极易引发行数爆炸。比如两张各 10 万行的表没关联条件,结果就是 100 亿行中间集 —— 即使最终 LIMIT 10,MySQL 仍可能先算完再截断。

实操建议:

  • 所有 JOIN 必须有明确、带索引的 ON 条件;检查 EXPLAIN 中各表的 rows 是否合理,特别警惕驱动表(第一行)rows 过大
  • 小表驱动大表:让过滤后结果集更小的表做驱动表(STRAIGHT_JOIN 可强制顺序,但慎用)
  • 避免在 ONWHERE 中对关联字段做函数操作,例如 ON UPPER(a.name) = UPPER(b.name) 会让索引失效
  • 临时表场景下,先用 CREATE TEMPORARY table + 索引预处理中间结果,比直接多层嵌套 JOIN 更可控

如何安全地优化线上慢查询而不锁表

在大表上 ALTER TABLE 加索引,默认会锁表(尤其 MySQL 5.6 之前),而 pt-online-schema-change 或 MySQL 8.0+ 的 ALGORITHM=INPLACE 能规避这个问题,但仍有风险点。

实操建议:

  • 优先用 ALTER TABLE ... ADD INDEX idx_name ON (col) ALGORITHM=INPLACE, LOCK=NONE(需引擎支持,InnoDB 一般 OK);执行前查 INFORMATION_SCHEMA.INNODB_TRX 确认无长事务
  • pt-online-schema-change 时,务必加 --dry-run--test-db 先验证;注意它会创建触发器,高并发写入下可能拖慢源表
  • 慢查询优化后,用 SLOW_LOGperformance_schema.events_statements_summary_by_digest 对比前后 avg_timer_waitexec_count
  • 别只盯着单条 SQL:有时瓶颈在应用层——比如循环里查数据库(N+1 问题),这种必须改代码,加索引没用

真正卡住的往往不是语法或索引本身,而是 WHERE 条件里的业务逻辑歧义、时间范围误设、或 JOIN 后未及时 GROUP BY 导致聚合膨胀。上线前拿真实数据量 + 线上执行计划复现,比任何理论都管用。

text=ZqhQzanResources