EXPLAIN中rows最小的表才是实际驱动表,该值代表WHERE过滤后预估行数;LEFT JOIN左表强制驱动,INNER JOIN由优化器选rows最小者;大表被驱动说明过滤失效或统计过期,需ANALYZE TABLE。

EXPLAIN 里哪张表才是实际驱动表?
别信 SQL 里表的书写顺序,EXPLAIN 输出中 rows 最小的那张表才是优化器选的驱动表。这个值代表该表经过 WHERE 过滤后预估返回的行数,不是原始表大小。
-
LEFT JOIN下左表强制为驱动表,哪怕它的rows是 50 万,优化器也得照做 -
INNER JOIN下优化器会估算所有表过滤后的rows,挑最小的当驱动表 - 如果大表被标为驱动表(比如
rows显示 80 万),说明WHERE没生效、统计信息过期,或索引没建对,该跑ANALYZE TABLE了 - 用
STRAIGHT_JOIN强制顺序前,先确认你比优化器更懂数据分布——否则可能把 100 行驱动变成 10 万行驱动
被驱动表的 ON 字段必须单独建索引
只要某张表在 ON 子句里是“被查”的那一方(即内层循环),它的关联字段就必须有索引。没有索引,小表驱动也没用,照样全表扫。
- 例如
JOIN orders o ON u.user_id = o.user_id,orders.user_id必须有索引(主键、唯一、普通都行) - 复合条件
ON t1.a = t2.x AND t1.b = t2.y,t2上要建INDEX(x, y),顺序必须和ON中一致 -
INDEX(a, b, c)无法加速ON t2.b = ?,因为b不是最左前缀 -
LEFT JOIN的右表、RIGHT JOIN的左表最容易漏建索引,务必逐个检查ON字段是否已索引
字段类型不一致会让索引完全失效
哪怕只是 INT 和 UNSIGNED INT、VARCHAR(50) 和 VARCHAR(100)、或者一边是字符串一边是数字,MySQL 都可能触发隐式转换,导致被驱动表索引不走。
- 常见错误:
ON a.id = CAST(b.user_id AS SIGNED)、ON a.phone = b.mobile(一个存 VARCHAR,一个存 BIGINT) - 查
EXPLAIN时看type:要是ALL或index,基本就是隐式转换或索引没命中 - 用
SHOW CREATE TABLE对比两边字段类型,保持严格一致 - 避免在
ON字段上做函数操作,包括UPPER()、TRIM()、DATE()等
Java 层能做的三件实际事
Java 不控制物理执行,但能通过业务逻辑减轻数据库压力。重点不是写更“漂亮”的 SQL,而是减少它要干的活。
- 只查需要的字段,避免
SELECT *,尤其大表含TEXT或BLOB时,网络和内存开销翻倍 - 深度分页不用
LIMIT offset, size,改用游标分页(如WHERE id > last_id ORDER BY id LIMIT 100) - 小表结果集可控时(比如缓存里查出 200 个用户 ID),改用
IN或临时表方式查大表,变 JOIN 为单表查询 + 内存合并
真正卡住性能的,往往不是 JOIN 本身,而是驱动表没过滤干净、被驱动表缺索引、或者字段类型悄悄不一致——这些地方一错,小表驱动就成空谈。



















