驱动表是怎么选的
多表连接时优化器按成本模型挑驱动表,理想形态是小结果集做驱动表、被驱动表通过索引做等值查找。当统计信息失真或写法特殊时,优化器可能选大表驱动并对被驱动表做全表扫描,执行计划因此急剧恶化。此时可以短期用 STRAIGHT_JOIN 纠偏,但根因通常是被驱动表缺索引或统计陈旧。
基本信息
| 适用 | MySQL 5.7 / 8.0,重点关注 JOIN 查询 |
|---|---|
| 判断入口 | EXPLAIN 输出顺序、type、rows、Extra 列 |
| 纠偏手段 | STRAIGHT_JOIN、optimizer hint(JOIN_ORDER 等) |
| 根因治理 | 补索引、更新统计信息、改写过滤条件 |
观察与纠偏
观察与纠偏
-- 观察顺序:EXPLAIN 里先出现的表为驱动表
EXPLAIN SELECT o.id FROM appdb.users u JOIN appdb.orders o ON o.user_id = u.id WHERE u.city='Hangzhou'\G
-- 检查被驱动表的访问类型:目标应为 ref 或 eq_ref,而非 ALL
EXPLAIN FORMAT=JSON SELECT o.id FROM appdb.users u JOIN appdb.orders o ON o.user_id = u.id WHERE u.city='Hangzhou'\G
-- 临时强制顺序(谨慎使用,仅用于验证假设)
SELECT STRAIGHT_JOIN o.id FROM appdb.orders o JOIN appdb.users u ON o.user_id = u.id WHERE u.city='Hangzhou';
ANALYZE TABLE appdb.orders; # 统计信息陈旧时先更新再重新评估计划计划质量判断
| 观察项 | 理想情况 | 异常表现 |
|---|---|---|
| 输出顺序 | 过滤后行数少的表在前 | 大表做驱动表,后续步骤反复扫描 |
| 被驱动表 type | ref 或 eq_ref | ALL 全表扫描,成本随行数放大 |
| rows 估算 | 与实际数量接近 | 估算严重偏小,导致选错顺序 |
| Extra | Using index / Index lookup | Using join buffer、Using temporary 说明无法走索引 |
| join buffer 使用 | 越少越好 | 大量 BNL 连接,内存换时间的信号 |
典型使用场景
- 慢 JOIN 排查:确认驱动表是否选错,被驱动表是否走得动索引
- 新增索引后验证计划是否按预期改变
- 历史数据分布变化导致计划退化,先更新统计再评估
- 分页查询在大偏移量下变慢,检查连接与排序的组合
- 上线前评审:把连接顺序与访问类型纳入 SQL 审查清单
连接顺序是优化器基于成本的结果而不是原因:被驱动表缺索引才是根本问题,先补索引再做其他调整通常一步到位。
把 STRAIGHT_JOIN 长期固化在业务 SQL 里风险很高:数据分布一变,写死的顺序反而更慢,它只适合作为临时验证手段。