mysql执行SQL语句时的优化与查询重写

发布时间 - 2026-01-11 00:00:00    点击率:
EXPLAIN 是优化慢查询的必要起点,它揭示索引使用、临时表、排序等真实执行细节;盲目改SQL易跑偏方向。

为什么 EXPLAIN 看起来没用,但必须先跑一遍

很多人在慢查询出现后直接改 SQL,跳过 EXPLAIN,结果优化方向完全跑偏。MySQL 的执行计划决定实际走哪个索引、是否临时表、是否排序、是否回表——这些无法靠肉眼判断。EXPLAIN 输出里的 type(如 ALLindexrange)、keyrowsExtra(尤其是 Using filesortUsing temporary)才是真实瓶颈信号。

  • WHERE 条件字段没索引?type 很可能为 ALL,意味着全表扫描
  • key 为空但本应走索引?检查字段类型是否隐式转换(比如 varchar 字段用数字比较)
  • rows 远大于结果集数量?说明索引选择性差或统计信息过期,可运行 ANALYZE TABLE
  • Extra 出现 Using index condition 是好现象;Using where; Using index 表示覆盖索引;而 Using temporary; Using filesort 基本等于性能红灯

JOIN 顺序不对,STRAIGHT_JOIN 有时比优化器更靠谱

MySQL 5.7+ 的优化器通常能选较优 JOIN 顺序,但在多表关联(尤其 4 张以上)、小表驱动大表逻辑明确、或存在复杂子查询嵌套时,它可能误判驱动表。此时强制顺序反而更快。

  • 默认 JOIN 是让优化器决定;STRAIGHT_JOIN 强制按 SQL 中从左到右的顺序连接
  • 适用场景:已知某张表过滤后只剩几行(如 WHERE order_status = 'paid' 后仅 10 条),就该让它当驱动表
  • 注意:STRAIGHT_JOIN 只影响 JOIN 顺序,不影响单表访问方式(仍依赖索引)
  • 副作用:如果数据分布突变(比如某天突然有 10 万条未支付订单),硬编码的顺序会恶化性能,需配合监控
SELECT STRAIGHT_JOIN u.name, o.total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2025-01-01';

OR 拆成 UNION ALL 真的有用,但得看条件是否独立

WHERE 中含多个 OR 条件且涉及不同字段(如 status = 'draft' OR created_by = 123),MySQL 往往放弃使用索引,退化为全表扫描。拆成 UNION ALL 可让每个分支单独走索引。

  • 必须满足:各分支返回列结构一致、无重复逻辑、结果集不需去重(否则用 UNION,但性能损失更大)
  • 无效场景:两个条件共用同一字段(如 id = 1 OR id = 2),这种本就能走索引,拆了反而增加开销
  • 注意 UNION ALL 不合并结果顺序,如需全局排序,必须在外层加 ORDER BY,且无法利用内层索引排序
  • 若分支间有大量重叠数据,UNION ALL 会放大网络/内存传输量
SELECT id, title FROM posts WHERE status = 'published'
UNION ALL
SELECT id, title FROM posts WHERE author_id = 99;

子查询改写为 JOINLATERAL(MySQL 8.0.14+)更可控

相关子查询(如 SELECT * FROM orders WHERE user_id IN (SELECT id FROM users WHERE region = 'CN'))容易触发 N+1 扫描;而派生表(FROM (SELECT ...) AS t)若没被物化或无法下推条件,也可能低效。

  • IN / EXISTS 子查询优先转为 JOIN:只要逻辑等价且不引入重复行,JOIN 通常更稳定
  • MySQL 8.0.14+ 支持 LATERAL,适合“每行调用一个动态子查询”的场景(如取每个用户的最新订单),避免传统 JOIN 的笛卡尔积膨胀
  • JOIN 替代子查询时,务必检查 GROUP BY 或聚合是否被意外消除——加 DISTINCT 或调整关联条件
  • 子查询中含 LIMITORDER BY?基本无法安全转 JOIN,老老实实用 LATERAL 或应用层分步查

真正棘手的不是语法改写,而是确认语义不变——比如 NULL 值处理、空结果集行为、重复键合并逻辑,这些地方一疏忽,数据就错了。


# mysql  # 编码  # ai  # sql语句  # 隐式转换  # 为什么  # sql  # NULL  # select  # union  # using  # 笛卡尔  # 拆成  # 尤其是  # 多个  # 才是  # 很多人  # 更大  # 但在  # 一遍  # 错了 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 制作电商网页,电商供应链怎么做?  Laravel怎么生成二维码图片_Laravel集成Simple-QrCode扩展包与参数设置【实战】  详解Nginx + Tomcat 反向代理 如何在高效的在一台服务器部署多个站点  如何在腾讯云服务器上快速搭建个人网站?  详解Android中Activity的四大启动模式实验简述  七夕网站制作视频,七夕大促活动怎么报名?  北京专业网站制作设计师招聘,北京白云观官方网站?  网站制作免费,什么网站能看正片电影?  Laravel如何与Docker(Sail)协同开发?(环境搭建教程)  使用Dockerfile构建java web环境  如何在 Pandas 中基于一列条件计算另一列的分组均值  网站制作大概多少钱一个,做一个平台网站大概多少钱?  如何快速重置建站主机并恢复默认配置?  Laravel如何记录自定义日志?(Log频道配置)  简单实现Android文件上传  如何使用 Go 正则表达式精准提取括号内首个纯字母标识符(忽略数字与嵌套)  php8.4header发送头信息失败怎么办_php8.4header函数问题解决【解答】  Python高阶函数应用_函数作为参数说明【指导】  标题:Vue + Vuex + JWT 身份认证的正确实践与常见误区解析  LinuxShell函数封装方法_脚本复用设计思路【教程】  如何快速搭建安全的FTP站点?  EditPlus中的正则表达式 实战(2)  Laravel如何实现邮箱地址验证功能_Laravel邮件验证流程与配置  Laravel中的withCount方法怎么高效统计关联模型数量  Laravel如何实现多语言支持_Laravel本地化与国际化(i18n)配置教程  Win11关机界面怎么改_Win11自定义关机画面设置【工具】  中山网站制作网页,中山新生登记系统登记流程?  jQuery中的100个技巧汇总  Python自然语言搜索引擎项目教程_倒排索引查询优化案例  Linux安全能力提升路径_长期防护思维说明【指导】  Laravel如何获取当前登录用户信息_Laravel Auth门面使用与Session用户读取【技巧】  如何在IIS7中新建站点?详细步骤解析  高端建站如何打造兼具美学与转化的品牌官网?  Laravel如何使用模型观察者?(Observer代码示例)  如何在自有机房高效搭建专业网站?  JavaScript中如何操作剪贴板_ClipboardAPI怎么用  如何快速搭建二级域名独立网站?  如何在香港免费服务器上快速搭建网站?  如何在 Telegram Web View(iOS)中防止键盘遮挡底部输入框  微信小程序 闭包写法详细介绍  如何正确选择百度移动适配建站域名?  Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】  Google浏览器为什么这么卡 Google浏览器提速优化设置步骤【方法】  Python文件流缓冲机制_IO性能解析【教程】  如何快速查询域名建站关键信息?  Laravel Eloquent关联是什么_Laravel模型一对一与一对多关系精讲  Laravel Blade模板引擎语法_Laravel Blade布局继承用法  武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?  Python文件异常处理策略_健壮性说明【指导】  Laravel如何升级到最新的版本_Laravel版本升级流程与兼容性处理