驱动表选错会导致百万次随机I/O,因MySQL默认Nested Loop Join需外层每行都查内层;关键看过滤后驱动表行数、被驱动表ON字段索引及类型一致性,而非物理表大小。

驱动表选错直接触发百万次随机I/O
MySQL默认用Nested Loop Join,外层每行都要去内层查一次。如果驱动表过滤后还有100万行,哪怕被驱动表有索引,也要执行100万次独立的索引查找——每次都是磁盘随机访问,不是顺序读。而如果驱动表只有1000行,总I/O量就差三个数量级。
常见错误现象:EXPLAIN里看到被驱动表的type是ALL或index、rows列数值远超预期、Extra出现Using join buffer,基本就是驱动表太大 + 被驱动表没索引双重暴击。
- 别只看物理表大小,重点看
WHERE过滤后实际参与JOIN的行数 -
LEFT JOIN左表强制驱动,右表再小也无法“翻身”,此时必须加强左表的WHERE条件 - 用
ANALYZE TABLE更新统计信息,否则优化器可能基于过期行数误判
被驱动表没索引=全表扫描,和驱动表大小无关
哪怕你把小表放前面,只要被驱动表的ON字段没索引,MySQL照样对它全表扫描。更隐蔽的是类型不匹配:比如a.id是INT,b.user_id是VARCHAR,MySQL会隐式转换,索引直接失效,key列显示NULL。
实操建议:
- 用
SHOW CREATE TABLE核对两表JOIN字段的类型、长度、是否允许NULL,必须严格一致 - 索引要建在被驱动表上,不是驱动表——例如
SELECT * FROM orders JOIN users ON orders.user_id = users.id,优先在orders.user_id建索引 - 联合索引不要依赖最左前缀;如果
ON只用到user_id,就单独建INDEX idx_user_id (user_id)
STRAIGHT_JOIN不是银弹,用错反而更慢
STRAIGHT_JOIN强制按SQL书写顺序执行,绕过优化器判断。但它只适用于INNER JOIN,对LEFT JOIN无效(语义已锁定驱动方向)。而且一旦表数据分布变化、统计信息过期,硬编码的顺序可能从最优变最差。
使用前提:
- 确认优化器确实选错了——通过
EXPLAIN ANALYZE对比真实执行耗时 - 被驱动表的
ON字段已有高效索引,且驱动表过滤后结果集稳定可控 - 线上环境需配合监控,避免某天因数据倾斜导致
STRAIGHT_JOIN把大表当驱动表
真正该盯住的三个点,而不是语法顺序
驱动表大小只是表象,底层性能取决于“每次驱动行要访问多少数据页”。这个数字藏在EXPLAIN的key_len、ref和状态变量Handler_read_next里。很多同学调了半天join_buffer_size,却没发现key_len是0——说明根本没走索引。
优先检查:
-
EXPLAIN中被驱动表的type是否为ref/eq_ref,key列是否非NULL - 驱动表的
WHERE条件是否能用上索引,有高选择性(如时间范围、状态枚举) - 多表JOIN时中间结果集是否雪球式膨胀,尤其
LEFT JOIN链式连接
最容易被忽略的是字段类型和字符集是否完全一致——差一个UNSIGNED、少一个COLLATE utf8mb4_0900_as_cs,索引就白建了。



















