驱动表选错会导致查询慢几十至几百倍,因大表被当作外层循环;explain第一行才是真实驱动表,inner join由优化器选择,left join左表强制为驱动表;被驱动表必须建索引且字段类型严格一致。

驱动表选错,查询可能慢几十倍甚至几百倍——不是因为SQL写得不对,而是MySQL执行时把大表当外层循环,每行都触发一次大表扫描。
EXPLAIN第一行才是真实驱动表,别信FROM顺序
很多人以为FROM small_table JOIN big_table就等于small_table驱动,其实优化器会重排。唯一可信的是EXPLAIN输出的第一行:它对应的就是实际驱动表。如果第一行显示type=ALL且rows=500000,哪怕你写了小表在前,它也已经被当成驱动表了。
- INNER JOIN下,优化器自由选择;LEFT JOIN下,左表强制为驱动表,无法绕过
-
rows列反映优化器估算的“过滤后驱动行数”,不是物理行数——所以ANALYZE TABLE必须定期跑 - 用
EXPLAIN FORMAT=TREE(MySQL 8.0+)能看清嵌套结构,最外层节点即驱动表
被驱动表没索引,小驱动表也救不了命
驱动表只控制循环次数,被驱动表才决定每次循环花多久。哪怕驱动表只剩10行,如果被驱动表的ON字段没索引,就得扫10 × 被驱动表总行数。
- 索引必须建在被驱动表上,比如
SELECT * FROM orders JOIN users ON orders.user_id = users.id,优先建INDEX idx_user_id (user_id)在orders表 - 字段类型必须严格一致:
INT对UNSIGNED INT、VARCHAR(50)对VARCHAR(100)、字符集或COLLATION不匹配,都会导致隐式转换和索引失效 - 复合
ON条件如ON t1.a = t2.x AND t1.b = t2.y,被驱动表需建INDEX(x, y),顺序不能颠倒
LEFT JOIN里“小表放左边”是伪命题
LEFT JOIN语法强制左表为驱动表,优化器无权交换。如果你写big_table LEFT JOIN small_table,MySQL必须全扫big_table——哪怕small_table有完美索引也白搭。
- 真正有效的做法是:提前用
WHERE过滤左表,把驱动结果集压到最小,比如加WHERE big_table.created_at > '2026-09-01' - 如果业务逻辑允许,改写成
INNER JOIN,把选择权交还给优化器 - 绝对避免在
WHERE里写右表字段(如WHERE small_table.status = 'active'),这会让语义退化为INNER JOIN,但执行计划仍按LEFT JOIN走,徒增NULL处理开销
最容易被忽略的点是:你以为的“小表”,在优化器眼里可能因统计不准、缺索引或类型不一致,被当成大表处理——所以别猜,EXPLAIN + ANALYZE TABLE + SHOW CREATE TABLE三件套必须一起用。










