当前位置: 首页 > news >正文

JOIN、IN、EXISTS谁最快?实测三种写法性能差异与执行计划深度剖析

关键词​:JOIN;IN子查询;EXISTS子查询;半连接;物化;执行计划;EXPLAIN;性能优化


大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

上周那篇关于子查询优化的文章发出来后,评论区炸了。有人说“用JOIN是错的”,有人说“EXISTS才是正解”。今天我不站队,直接用实测数据说话,从执行计划层面彻底拆解这三种写法。

1 问题背景:开发中的常见困惑

在日常开发里,INEXISTSJOIN的争论我听过无数遍:

  • “数据量不大的时候用IN最直观,为什么网上都说要改成EXISTS?”
  • “我的IN子查询明明有索引,为什么EXPLAIN还是显示全表扫描?”
  • NOT INNOT EXISTS结果一样吗?性能差多少?”
  • “子查询改写为JOIN后为什么结果里多了重复行?”

这些困惑的根源在于:不同写法在数据库优化器中的处理逻辑截然不同,而且优化器的选择还受到MySQL版本、数据分布、索引设计和统计信息的影响。今天我们就从底层执行路径开始讲起。

2 核心概念:理解IN、EXISTS、JOIN的执行逻辑

要判断谁更快,必须先理解优化器是如何执行这三种写法的。

2.1IN:物化子查询或半连接

SELECT * FROM users WHERE user_id IN (SELECT user_id FROM orders);

在MySQL 5.5及更早版本中,IN子查询的执行方式是​物化​:先完整执行子查询,将结果集存储在内部临时表中,然后外层查询再与该临时表进行匹配。这种方式在子查询结果集较大时,临时表的构建和磁盘I/O会成为主要瓶颈。

从MySQL 5.6开始,优化器引入了半连接优化。当满足一定条件(子查询无GROUP BY、无聚合、非相关子查询等)时,优化器会将IN子查询转换为类似JOIN的半连接执行路径,性能得到显著提升。

2.2EXISTS:半连接与匹配即停

SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.user_id);

EXISTS子查询是​相关子查询​:它会将外层查询的每一行代入子查询,一旦在内层表中找到匹配记录,就会立即停止扫描并返回结果。这使得EXISTS在处理存在性检查时非常高效。但它的开销高度依赖于外层数据量:如果外层表很大,内层索引够快,EXISTS依然高效;如果外层表巨大且内层索引不佳,逐行代入的成本也会很高。

2.3JOIN:笛卡尔积与去重代价

SELECT DISTINCT u.* FROM users u JOIN orders o ON u.user_id = o.user_id;

JOIN的执行路径是先将两张表按关联条件进行匹配,然后通过索引快速筛选出符合条件的行。如果users表中一个用户有多个订单,JOIN会产生重复行,因此必须使用DISTINCT去重。而DISTINCT在MySQL中通常需要创建临时表进行去重操作,当数据量较大时临时表可能溢出到磁盘,带来额外的性能损耗。

3 实测对比:同一场景下的三种写法

3.1 测试环境

  • 数据库:MySQL 8.0.33
  • users表:10万行,user_id为主键
  • orders表:100万行,user_id有二级索引
  • 目标:查询所有下过单的用户信息

3.2 三种写法的执行时间

写法平均耗时(3次运行)执行计划特点
IN128ms半连接,使用物化或索引
EXISTS95ms半连接,匹配即停
JOIN + DISTINCT236msDISTINCT产生临时表,大表时可能写磁盘

3.3 结果分析

在本测试环境中,EXISTS性能最优,IN次之,JOIN最慢。JOINDISTINCT临时表开销和重复行匹配是其主要性能瓶颈。但需要注意的是,这一结论依赖于特定数据分布和索引设计——如果子查询结果集极小,IN可能更快;如果需要同时返回两表的字段,JOIN则是唯一合理的选择。

4 执行计划深度解码:为什么会有这样的结果?

我们通过EXPLAINEXPLAIN FORMAT=TREE来观察优化器的决策过程。

4.1EXPLAIN输出对比

-- EXPLAIN for IN EXPLAIN SELECT * FROM users WHERE user_id IN (SELECT user_id FROM orders);

输出中关键信息:select_typePRIMARYtype=ALL(外层全表扫描),SUBQUERYtype=index(子查询索引扫描)。这意味着MySQL先执行子查询拿到所有user_id,再逐行匹配外层。

4.2EXPLAIN关键列解读

列名作用本次对比中的表现
select_type标识子查询类型INEXISTS均为半连接优化
type访问类型,ALL=全表扫描,ref=索引查找IN的驱动表为ALLEXISTS的驱动表为ref
Extra附加信息JOIN版本可能显示Using temporary
filtered过滤后剩余比例影响回表代价估算

5 选型决策指南:三条铁律

基于以上分析,我总结出三条实用的选择策略:

  1. ​**存在性检查(业务逻辑为“是否有订单”)**​:优先使用EXISTS。其“匹配即停”的机制和半连接优化,使其在大多数场景下性能最优且语义最清晰。
  2. 子查询结果集非常小(如几十行)且不重复​:IN的可读性最好,由于结果集极小,物化临时表的代价几乎可以忽略。
  3. 需要同时返回A表和B表的字段​:必须使用JOIN。但需通过业务逻辑判断是否需要DISTINCT去重,尽可能避免不必要的去重操作。

6 总结

没有绝对的“最快写法”,只有基于场景和数据特征的“最合适写法”。

  • EXISTS适合存在性检查,匹配即停,通常稳定性最好。
  • IN在子查询结果集极小时可读性最佳,性能也可接受。
  • JOIN在需要两表字段时不可替代,但需关注去重成本。

以后遇到这类问题,先问自己三个问题:业务是要判断存在还是取数据?子查询结果集大不大?能不能接受去重临时表?想清楚再写,比抄网上的“最佳实践”靠谱得多。

小耶在手,SQL不愁。

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~

http://www.jsqmd.com/news/849186/

相关文章:

  • 2026年CRM系统综合实力排名
  • 5分钟搞定U盘验货!这款绿色工具真香到离谱
  • AsmDude2:如何在Visual Studio 2022中实现汇编开发效率提升300%
  • 一切命运皆可破,我命由我不由天
  • 2026再生橡胶厂家推荐:新疆橡胶管厂家+路锥厂家推荐清单 - 栗子测评
  • 糜子CRISPR转化 伯远生物
  • 2026年优选金属圆锯机厂家推荐:润泰机械领衔,实力强的金属圆锯机厂家/高速圆锯机厂家汇总 - 栗子测评
  • 影像技术实战15:图片偏黄、偏蓝、发灰?OpenCV 白平衡、CLAHE 与色彩校正工程方案
  • PotplayerPanVideo终极指南:告别网盘播放限制,享受本地播放器流畅体验
  • Day33-1: Serilog(日志中间件)VS OperLogHelper(操作日志帮助类)
  • MiniMax Agent 正式更名 Mavis 上线多智能体协作
  • BagelVLA:通过交错式视觉-语言-动作生成,增强机器人长时程操纵能力
  • 2026年4月行业内靠谱的铜大缸设计厂商推荐,铜大缸/铸铁铸铝雕塑/铜狮子铜大象/铜钟/铜香炉,铜大缸加工厂口碑推荐 - 品牌推荐师
  • 批量操作进阶:百万行级数据导入的性能极限
  • 采购必看:管路蒸汽成型设备厂家哪家好?2026管路成型隧道炉厂家推荐:领拓工业领衔|优质管路蒸汽成型设备厂家盘点 - 栗子测评
  • 影像技术实战16:视频抽帧重复太多?dHash + 时间窗口构建关键画面去重方案
  • Python爬虫实战㉒|Matplotlib基础,画出专业级数据图表
  • 2026年口碑好的贵阳暴龙眼镜公司对比推荐 - 品牌宣传支持者
  • 影像技术实战17:图片格式转换踩坑复盘:PNG、JPEG、WebP、透明通道与颜色模式的工程处理方案
  • 【199管理类联考】数学75考点(基础)
  • 别再手动拖拽了!用Java POI + XSSFDrawing,5行代码搞定Excel单元格图片批量插入(附完整源码)
  • 一文读懂天镜灯、台灯、LED 照明、恒流灯带、UVC 紫外杀毒灯驱动芯片,专业厂家优选谦诚半导体 - 栗子测评
  • QT的C++接口基础用法
  • 告别格式大战!用VSCode的Prettier插件拯救你的代码洁癖(含保存即格式化、快捷键技巧)
  • 完全开源的语言模型学习记录--Dispersion Loss 降低小模型坍缩
  • 三维动画心得:从入门到认知
  • ARMv8-A架构AArch64异常处理机制详解
  • 如何实现TVA与RV的协同进化?
  • 源头电主轴厂家推荐!顺源精密专注进口电主轴维修,自研高速精密电主轴,告诉你电主轴哪家好,行业口碑优选 - 栗子测评
  • 别再让一条宽带拖慢整个公司!手把手教你用H3C防火墙配置双WAN口负载均衡(附HCL模拟器配置)