惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
小众软件
小众软件
S
SegmentFault 最新的问题
人人都是产品经理
人人都是产品经理
博客园 - 【当耐特】
博客园 - 三生石上(FineUI控件)
C
Check Point Blog
S
Schneier on Security
Microsoft Azure Blog
Microsoft Azure Blog
N
Netflix TechBlog - Medium
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
罗磊的独立博客
有赞技术团队
有赞技术团队
V
V2EX
Y
Y Combinator Blog
博客园 - 叶小钗
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
F
Fortinet All Blogs
W
WeLiveSecurity
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog
The Cloudflare Blog
S
Security @ Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
MyScale Blog
MyScale Blog
Hugging Face - Blog
Hugging Face - Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
PCI Perspectives
PCI Perspectives
H
Heimdal Security Blog
Schneier on Security
Schneier on Security
Security Latest
Security Latest
AWS News Blog
AWS News Blog
月光博客
月光博客
Security Archives - TechRepublic
Security Archives - TechRepublic
Recent Announcements
Recent Announcements
Google DeepMind News
Google DeepMind News
博客园 - Franky
Cisco Talos Blog
Cisco Talos Blog
T
Threat Research - Cisco Blogs
M
MIT News - Artificial intelligence
T
Troy Hunt's Blog
N
News and Events Feed by Topic
Cloudbric
Cloudbric
Scott Helme
Scott Helme
云风的 BLOG
云风的 BLOG
Attack and Defense Labs
Attack and Defense Labs

博客园 - 郭慕荣

彻底搞懂 Codex Skill 与插件:区别、试用、核心作用、项目落地全指南(程序员实战向) Codex Skill 和插件到底有什么区别?程序员如何在项目中落地使用 claudecode 配置防止配置文件泄露总结 Claude code分析外部文件总结 Claude code三种模式详解 hasCompletedOnboarding详解 Claudecode 有哪些常用的命令 claude.md文件详解 claude code 的setting文件详解 Docker部署zookeeper总结 Mac中git ssh的配置(GitLab) Java 类加载机制 面试题(一) 如何监控和调优JVM性能? jvm常用的参数有哪些? 怎么配置? hashmap 和currenthashmap 的原理?详解一下 aop在项目中使用的场景?怎么使用? Java dubbo spring springboot中的spi机制 spring中常见的两种代理模式 Redis的zset 面试汇总 drools 规则引擎在线化配置 springcloud中常用的注解详解 springcloud中网关gateway总结 spring是怎么解决循环依赖的? MySQL 死锁 怎么处理? 在写left join的时候 是大表在左侧 还是小表在左侧(二) nacos客户端(接口调用者)如何感知被调用服务下线? (二) nacos客户端(接口调用者)如何感知被调用服务下线?(一) 在MySQL中 redolog undolog binlog 写入的场景,顺序
在写left join的时候 是大表在左侧 还是小表在左侧(一)
郭慕荣 · 2025-10-21 · via 博客园 - 郭慕荣

好——这个问题既是面试高频点,也是写生产 SQL 时必须搞清楚的性能细节。下面把概念 + 引擎实现 + 左连接(LEFT JOIN)语义影响 + 实际优化建议与排查清单都讲清楚,你能直接在面试里条理化回答,也能在工程中落地排查与优化。

结论先说一句话

  • 性能上通常希望“驱动(build/probe)侧是小表”:无论是嵌套循环(Nested-Loop)还是哈希(Hash)连接,数据库都倾向于对小表做构建或做外层遍历以减少工作量。

  • 但对 LEFT JOIN(外连接)要小心:外连接的语义限制了优化器随意重排表顺序,因此如果你想让小表做“驱动”,需要注意写法或使用 hint/STR AIGHT_JOIN 强制。Database Administrators Stack Exchange+1


1) 先看:数据库常见的连接算法(会影响“谁做”)

  • 索引嵌套循环(Index Nested-Loop):外层逐行扫描,内层用索引按键快速查找。适合外层较小或内层有合适索引的场景。MySQL 传统上大量使用这种方式。Medium

  • 哈希连接(Hash Join):先对“较小的一方”构建哈希表(build),再扫描“较大的一方”并 probe 哈希表匹配。MySQL 从 8.0.18 起支持 hash join(主要用于等值内连接)。因此哈希连接更要求把小表作为 build side。MySQL开发者区+1

  • 排序合并(Sort-Merge Join):对两边排序后线性合并(MySQL 对此支持受限,具体实现/可用性与版本有关)。Medium

结论:不论哪种算法,把更小、选择性更高、或能建立索引的表放在“build/外层”通常更高效(少 I/O、少 probe/比较)。


2) LEFT JOIN 的语义与优化器重排问题(面试必问点)

  • LEFT JOIN a LEFT JOIN b 的语义:左表的每一行必须保留(即使右表无匹配,用 NULL 填充)。这语义限制了优化器能做的重排(不能随意把右表当做外层表来改变逻辑结果)。MySQL 优化器在重写/重排连接时要保证外连接的语义不被破坏,因此在某些情况下无法或不会把右表提前为驱动表MySQL开发者区+1

  • 实战结果:对 LEFT JOIN,想让小表“先被处理”需要注意

    • 如果你用的是内连接(INNER JOIN),优化器可自由重排,通常会选最优驱动顺序(常把小表做 build)。

    • LEFT JOIN 有语义约束,优化器可能必须先读取左表,然后为每行查右表(外层为左表),这在左表非常大时可能代价高(大量探针)。因此如果左表很大而你希望效率更高,需考虑改写或调整索引/提示。Stack Overflow+1


3) 实际建议(工程化可执行的规则)

下面按“你能马上应用”的清单写,便于面试与工程落地。

A. 优先保证索引(最重要)

  • 无论 join 顺序,右表(被查找表)的 join 列必须有合适索引,这样即便外层是大表,内层也能快速通过索引定位匹配,避免全表扫描。

B. 让“更小的表作为 build/驱动”——如何做到

  • 如果是 INNER JOIN:数据库会倾向于最优驱动顺序(小表做 build)。只需确保统计信息和索引正确。

  • 如果是 LEFT JOIN,但语义上你可以把逻辑改成等价的 INNER JOIN(例如把过滤条件提早到 ON/WHERE,使行必然匹配),那就改为 INNER JOIN。否则:

    • 重写查询:用子查询先把小表聚合/筛出(derived table/临时表),对其建立索引或物化,再 join 大表。

    • 用 STRAIGHT_JOIN 强制顺序(MySQL):SELECT /*+ STRAIGHT_JOIN */ ...STRAIGHT_JOIN 关键字,让写表顺序成为驱动顺序(小心语义与正确性)。

    • 利用 optimizer hints(MySQL 8.0+)JOIN_ORDER() 等 hint(视版本)来影响重排。

C. 对于 MySQL 8+:若能用 Hash Join,效果更好

  • 在 MySQL 8.0.18+ 若走 hash join,查询会先对小表构建 hash,然后扫描大表 probe;这符合“小表先做 build”的规则。检查 EXPLAIN 看是否采用 hash join。MySQL开发者区

D. 避免产生过多临时结果与排序

  • 如果 JOIN 前没有先筛选,会乘法级别扩大中间行数(导致 temp table / disk spill)。尽量把过滤(WHERE 条件)尽早下推或先在 derived table 中做聚合/筛选,减少中间集大小。

E. 监控与调整参数(MySQL 特有)

  • 若查询使用大量全表扫描,MySQL 可能使用 join_buffer(用于没有索引时的块嵌套)。可查看 join_buffer_size,但更优是补索引或改写查询。

  • 对 HASH JOIN,注意内存和 hash 表大小(由服务器自动控制,但需要关注内存使用)。use-the-index-luke.com


4) 排查步骤(在生产中你应该怎么做)

  1. 先用 EXPLAINEXPLAIN ANALYZE(MySQL 8)查看执行计划(观察 join type、possible_keys、rows、Extra 是否用 Using where/Using index/Using temporary)。

  2. 看 optimizer 是否选择 nested-loop、hash join 或全表扫描。若是 nested-loop 且外层是大表且内层没有索引 → 性能问题的常见原因。Medium

  3. 若 LEFT JOIN 导致外层大表全表扫描且结果慢,考虑:加索引 / 将小表先聚合成临时小表 / 改写成 EXISTS(有时 EXISTS 更高效)或使用 STRAIGHT_JOIN 强行改变执行顺序测试效果(注意语义)。Database Administrators Stack Exchange+1

  4. 做对比测试(改写前后在测试库跑 explain / timing),不要盲目在生产直接改写。


5) 常见面试问答要点(简短可背)

  • 问:LEFT JOIN 时哪个表放左边更好?
    答:语义上左边是要保留的表;性能上你通常希望 小表作为 build / 驱动(这样 probe 次数少)。但 LEFT JOIN 的语义可能禁止优化器把右表先做驱动,因此如果左表很大需要改写查询或使用 hint/临时表来让小表先被处理。务必保证被 probe 的表有索引,并用 EXPLAIN 验证执行计划。Database Administrators Stack Exchange+1

  • 问:MySQL 支持哈希连接吗?会影响 join 顺序吗?
    答:从 MySQL 8.0.18 开始支持 Hash Join(主要用于等值内连接),Hash Join 会把小表做 build并对大表 probe;因此在能走 hash join 时,使小表为 build side 性能更优。MySQL开发者区+1


6) 小结 — 实战优先级

  1. 先看执行计划(EXPLAIN) —— 确认 join 算法、是否有索引、rows 估算。

  2. 保证 join 列有索引(特别是被查表的列)。

  3. 尽量让小表做 build/驱动:对于 INNER JOIN 由优化器处理;对于 LEFT JOIN,若左表很大且性能差,考虑改写(子查询/derived)或 hint/STRAIGHT_JOIN。

  4. 考虑 Hash Join(MySQL 8.0.18+):当可用时对大表/小表组合很有利(小表作 build)。

  5. 用实际测量(EXPLAIN ANALYZE / timing)来验证每一步,别凭直觉改造查询。