JOIN字段必须加索引,否则被驱动表将全表扫描;索引需建在ON子句列上,注意类型一致、最左前缀及避免函数操作。

JOIN 字段必须加索引,否则直接变全表扫描
MySQL 在执行 INNER JOIN、LEFT JOIN 等连接操作时,如果 ON 条件中的字段没有索引,优化器大概率放弃使用索引,对被驱动表做全表扫描。哪怕主表只返回 1 行,被驱动表仍可能扫几万行。
常见错误现象:EXPLAIN 结果中 type 是 ALL 或 index,rows 值远超预期,Extra 出现 Using join buffer (Block Nested Loop)。
- 索引必须建在
ON子句里实际参与比较的列上,不是SELECT列 - 复合索引要注意最左前缀:若写
ON t1.a = t2.a AND t1.b = t2.b,则t2(a,b)有效,t2(b,a)通常无效 - 关联字段类型要严格一致:比如
INT和BIGINT隐式转换会导致索引失效;VARCHAR(50)和VARCHAR(100)一般不影响,但字符集或排序规则不同(如utf8mb4_0900_as_csvsutf8mb4_general_ci)会拒绝走索引
驱动表选择影响索引是否生效
MySQL 的嵌套循环连接(Nested-Loop Join)中,先查的表叫驱动表,后查的叫被驱动表。优化器通常选小结果集作驱动表,但有时判断失误,导致本该走索引的被驱动表被迫全表扫描。
可强制指定驱动顺序:用 STRAIGHT_JOIN(仅限 INNER JOIN),把预估更小的表放在 FROM 后,大表放 JOIN 后,并确保大表的 ON 字段有索引。
SELECT STRAIGHT_JOIN a.id, b.name FROM small_table a JOIN big_table b ON a.ref_id = b.id;
- 检查
EXPLAIN的table列顺序,确认哪张是驱动表 -
rows值大的那张表,务必确保其ON字段有索引 - 避免在
ON条件中对字段做函数操作,例如ON YEAR(t1.create_time) = t2.year会让t1.create_time索引失效
覆盖索引 + JOIN 可避免回表,但需注意字段顺序
当被驱动表的查询字段全部包含在某个索引中(即覆盖索引),MySQL 就不用回主键索引捞数据,能显著减少 I/O。但这要求索引把 ON 字段放在前面,查询字段放后面。
例如:需要 SELECT b.name, b.status FROM a JOIN b ON a.bid = b.id,则推荐建索引 b(id, name, status),而不是 b(name, status, id) —— 后者无法用于 ON 匹配。
- 复合索引中,等值条件字段(
=)放最左,范围条件(>,BETWEEN)放中间,查询字段放最后 - 如果
SELECT中用了ORDER BY b.name且想避免 filesort,索引需包含name并满足排序需求(如b(id, name)可支撑ORDER BY name,但前提是id是等值过滤) -
WHERE条件里的单表过滤字段也应纳入索引,例如WHERE a.type = 1 AND b.status = 'active',则b(id, status, name)更优
多表 JOIN 时,中间表索引容易被忽略
三张及以上表连接时,MySQL 会按一定顺序执行两两 JOIN。第二步 JOIN 的“被驱动表”可能是第一步的结果集(临时表),但更多时候仍是原始物理表。很多人只给首尾两张表建索引,漏掉中间表的 ON 字段索引。
典型场景:orders JOIN order_items ON orders.id = order_items.order_id JOIN products ON order_items.product_id = products.id。这里 order_items 是中间表,它既被 orders 驱动(需 order_id 索引),又被 products 驱动(需 product_id 索引)——两个字段都得单独或联合建索引。
- 用
EXPLAIN FORMAT=TREE(MySQL 8.0+)看真实执行计划,确认每一步的驱动/被驱动关系 - 中间表上建议建联合索引,如
order_items(order_id, product_id),兼顾两个方向的 JOIN - 避免
SELECT *,尤其在多表 JOIN 中,它会放大回表和临时表开销,让索引收益打折










