JOIN字段必须加索引,否则被驱动表将全表扫描;索引需建在ON子句列上,注意类型一致、最左前缀及避免函数操作。
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_cs vs utf8mb4_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 索引失效当被驱动表的查询字段全部包含在某个索引中(即覆盖索引),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) 更优三张及以上表连接时,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),兼顾两个方向的 JOINSELECT *,尤其在多表 JOIN 中,它会放大回表和临时表开销,让索引收益打折