大表左连接小表通常更慢,因LEFT JOIN强制左表为驱动表,1亿行扫描远超小表驱动的100次循环;仅当大表连接字段有唯一索引、小表无索引且查询带有效LIMIT时才可能例外。

大表左连接小表通常不会更快——相反,它大概率更慢,除非满足极少数特殊条件。 关键不是“谁在左”,而是“谁当驱动表”,而 LEFT JOIN 的左表就是强制驱动表。所以把大表放左边,等于主动让数据库用 1 亿行去循环匹配,这是性能杀手。
LEFT JOIN 的左表永远是驱动表,无法由优化器调整
MySQL(以及多数主流数据库)对 LEFT JOIN 不会重排驱动顺序。无论统计信息多准确、索引多完备,FROM big_table LEFT JOIN small_table 的执行逻辑永远是:
- 全扫
big_table每一行(比如 1 亿行) - 对每一行,去
small_table中查找匹配项(哪怕small_table只有 100 行,也要查 1 亿次)
即使 small_table 有索引,单次查找快,但 1 亿次 × 索引树跳转的 CPU 和缓存压力依然巨大。而反过来:FROM small_table LEFT JOIN big_table,外层只循环 100 次,总开销断崖下降。
什么情况下大表放左边反而不那么糟?
只有当以下**全部条件同时满足**时,大表在左才可能勉强可接受:
-
big_table的连接字段(ON后的列)有唯一索引(如主键或UNIQUE约束),且该索引能保证每次查找最多返回 1 行 -
small_table的连接字段没有索引,导致它被驱动时也得全表扫描 —— 此时交换顺序反而让小表承担全表扫描成本 - 查询加了
LIMIT且能命中索引下推(例如ORDER BY big_table.id DESC LIMIT 10),使实际扫描行数远低于总行数
注意:这属于例外场景,不能作为设计依据。日常开发中应默认规避。
别信“优化器会自动选小表”这种说法
对 INNER JOIN,优化器确实常选小表驱动;但对 LEFT JOIN,它无权改变语义。你写 FROM A LEFT JOIN B,就等于告诉数据库:“必须保留 A 全部数据”,优化器只能在这个约束下做有限优化(比如用索引加速 B 的查找),绝不会擅自改成 FROM B LEFT JOIN A —— 那结果语义完全不同。
常见误判来源:
- 看
EXPLAIN显示type: eq_ref就以为“走索引就快”,忽略了外层驱动行数本身已爆炸 - 测试数据量太小(比如两个表都只有几千行),掩盖了大表驱动的真实放大效应
- 没关掉
batched_key_access(BKA),误以为 BKA 完全消除了驱动表大小影响 —— 实际上 BKA 仍依赖驱动表结果集大小来分批,大驱动表会导致更多批次和更大临时缓冲区
真正该盯住的三个点,而不是表顺序
比起纠结“大表放左还是放右”,优先确认:
-
LEFT JOIN是否真的必要?能用INNER JOIN就别用LEFT JOIN—— 后者多出 NULL 补全逻辑 + 结果集膨胀风险 - 被驱动表(即
LEFT JOIN右侧的表)连接字段是否有高效索引?EXPLAIN中key列是否非NULL,rows是否接近实际匹配行数? - 驱动表本身能否通过
WHERE条件提前过滤?例如FROM big_table WHERE status = 'active' LEFT JOIN ...,先把驱动表压到几万行,比硬扛 1 亿行强十倍
驱动表大小决定循环基数,这是嵌套循环连接(Nested Loop)绕不开的底层事实。再好的索引,也救不了 1 亿次起步的外层迭代。










