为什么MySQL关联查询要“小表驱动大表”?深入解析与模拟面试复盘


场景模拟:一场技术面试的攻防战

面试官:你在简历里提到优化过大量SQL查询,那你说说为什么MySQL做关联查询时建议用小表驱动大表?

候选人:因为用小表作为驱动表可以减少外层循环的次数,比如用INNER JOIN时,外层表行数越少,内层表扫描次数就越少,性能更好。


面试官(追问):那什么是"驱动表"?MySQL是如何决定哪个表作为驱动表的?

候选人:驱动表是执行计划中首先被访问的表,决定了连接顺序。MySQL优化器会根据统计信息(如索引、行数、数据分布)自动选择。但我们可以用STRAIGHT_JOIN强制指定驱动表。


面试官(深入):假设两个表都没有索引,为什么这时候"小表驱动大表"的性能差异会更明显?

候选人:如果被驱动表无索引,内层循环每次都要全表扫描。假设驱动表有N行,被驱动表有M行: • 小表驱动(N=100, M=1万):总扫描次数 = 100次全表扫描(1万行)→ 100万行

• 大表驱动(N=1万, M=100):总扫描次数 = 1万次全表扫描(100行)→ 100万行

虽然总行数相同,但前者需要更多磁盘I/O(大表数据分散),且可能触发缓存淘汰。


面试官(陷阱题):那如果被驱动表有索引,小表驱动还有必要吗?

候选人:此时性能差异可能缩小,但仍有优化空间。例如: • 小表驱动时,内层走索引查询,每次查找是O(log M)复杂度。

• 大表驱动时,内层虽然也走索引,但外层循环次数更多,CPU开销可能更高。

此外,驱动表数据量小,更容易放入join_buffer(若使用Block Nested-Loop Join算法)。


面试官(底层原理):能解释下Block Nested-Loop Join(BNL)和Index Nested-Loop Join(INL)的区别吗?

候选人: • INL:被驱动表有索引时,内层循环直接通过索引定位数据,时间复杂度O(N log M)

• BNL:无可用索引时,将驱动表加载到join_buffer,批量匹配被驱动表,时间复杂度O(N * M)。此时用小表驱动可降低内存占用,减少磁盘扫描次数。


面试官(实战场景):如何判断一条关联查询是否使用了最优驱动表?

候选人:通过EXPLAIN查看执行计划: • 第一行出现的表即为驱动表。

• 关注type字段:ref/range表示索引有效,ALL表示全表扫描。

• 检查rows预估行数,对比实际数据量判断统计信息是否准确。


核心结论

  1. 性能本质:减少外层循环次数 + 利用索引降低内层循环代价。

  2. 优化器局限:统计信息过期、复杂过滤条件可能导致优化器误判。

  3. 判断标准: • 当被驱动表无索引 → 严格遵循"小表驱动大表"。

    • 当被驱动表有索引 → 优先保证内层循环走索引,驱动表选择次之。

  4. 终极方案:通过EXPLAIN验证执行计划,必要时用FORCE INDEXSTRAIGHT_JOIN干预。


面试官:不错,今天先聊到这儿。回去等通知吧。(暗中点头)


附录:经典优化公式

总查询代价 ≈ 驱动表查询成本 + 驱动表行数 × 单次被驱动表查询成本

通过降低驱动表的行数或降低被驱动表的单次查询成本(如加索引),可显著提升性能。


相关推荐
2601_9620639731 分钟前
Spring Boot拦截器(Interceptor)详解
java·spring boot·后端
明月_清风1 小时前
GitHub Actions 从入门到实战:一文搞懂 CI/CD 自动化
后端·ci/cd·github
foggyprojects1 小时前
AI 问数也要做行级权限:让 Codex 给销售查询加上权限
后端
千里码aicood2 小时前
flask基于数字人的储粮知识问答原型系统研究与实现
后端·python·flask
考虑考虑3 小时前
arthas使用
java·运维·后端
李昊哲小课3 小时前
Spring Boot 4 旅游主题实战教程 阶段三:数据持久化
spring boot·后端·mybatis·旅游
AI 编程助手GPT3 小时前
Bun 1.4 正式发布:从 Zig 改写为 Rust,内置浏览器、图片处理和并行测试
开发语言·人工智能·后端·ai·chatgpt
IT_陈寒3 小时前
Redis大KEY删除慢到手抖,这几个方法让我少熬一夜
前端·人工智能·后端
步行cgn4 小时前
传统 SSM 与 Spring Boot 开发对比:从配置地狱到约定优于配置
java·spring boot·后端