驱动表是explain第一行对应的表,而非sql书写顺序决定;被驱动表on字段必须有索引且类型、字符集、排序规则严格一致;left join左表强制为驱动表,straight_join慎用。

EXPLAIN里第一行就是驱动表,别信SQL书写顺序
MySQL优化器决定哪个表当驱动表,不是你写了FROM a JOIN b就一定是a驱动b。唯一可靠判断方式是看EXPLAIN输出的第一行——那一行对应的表就是实际驱动表。很多同学调了半天索引,发现key列非空、type是ref,但查询还是慢,就是因为没意识到驱动表其实是第二行那个大表。
常见错误现象:EXPLAIN中驱动表的rows列显示10万+,而被驱动表type是ALL或index;或者Extra里出现Using join buffer (Block Nested Loop),基本等于告诉你是驱动表太大 + 被驱动表没索引双重暴击。
被驱动表ON字段没索引,驱动表再小也白搭
驱动表只控制“循环次数”,被驱动表才决定“每次循环花多少时间”。哪怕驱动表过滤后只剩10行,如果被驱动表的关联字段没索引,MySQL就得对它全表扫描10次——等于10 × 被驱动表总行数的I/O。
-
SHOW CREATE TABLE核对两表JOIN字段类型、长度、是否允许NULL,必须严格一致(比如INT对BIGINT、VARCHAR(50)对VARCHAR(100)都可能让索引失效) - 字符集和排序规则(
COLLATION)也要一致,否则等值匹配无法走索引 - 索引建在被驱动表上,不是驱动表——例如
SELECT * FROM orders JOIN users ON orders.user_id = users.id,优先在orders.user_id建索引 - 联合索引别贪多,ON条件只用到
user_id,就单独建INDEX idx_user_id (user_id),别指望最左前缀能兜住
LEFT JOIN的驱动方向是语义锁定的,优化器不能改
LEFT JOIN左边的表强制为驱动表,不管它数据量多大、有没有WHERE条件。这是语法定义决定的,优化器无权重排。所以当你写FROM big_table LEFT JOIN small_table,哪怕small_table加了完美索引,MySQL也必须把big_table全扫一遍去驱动。
实操建议:
- 如果业务允许,优先用
INNER JOIN替代LEFT JOIN,给优化器留出选择空间 - 实在要用
LEFT JOIN,就在左表上加强WHERE过滤条件,并确保它能用上索引,把驱动结果集压到最小 - 避免在
LEFT JOIN后的WHERE里写右表字段(如WHERE b.status = 'active'),这会把逻辑退化成INNER JOIN,但执行计划仍按外连接走,徒增NULL行处理开销
STRAIGHT_JOIN不是救命稻草,用错反而雪上加霜
STRAIGHT_JOIN只对INNER JOIN有效,且绕过优化器判断。它适合你已确认优化器选错、被驱动表索引高效、驱动表过滤后行数稳定可控的场景。但一旦数据倾斜(比如某天某个user_id突然产生百万订单),硬编码的顺序会让性能断崖下跌。
真正该盯住的三个点:
-
EXPLAIN中被驱动表的type是否为ref或eq_ref,key列是否非NULL - 驱动表的
WHERE条件是否走索引,rows是否接近真实过滤后行数 -
key_len值是否合理——比如key_len是0,说明根本没走索引,调join_buffer_size毫无意义
复杂点在于:驱动表大小只是表象,底层性能取决于“每次驱动行要访问多少数据页”,这个数字藏在Handler_read_next状态变量里,而不是rows估算值中。











